Merge "Clean up PC QWERTY keyboard"
diff --git a/java/res/values-v18/emoji-categories.xml b/java/res/values-v18/emoji-categories.xml
new file mode 100644
index 0000000..17a2053
--- /dev/null
+++ b/java/res/values-v18/emoji-categories.xml
@@ -0,0 +1,922 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     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.
+-->
+
+<!-- Note: This emoji code point list is valid on JB-MR2 (API == 18).
+     There is another emoji code point list for KLP and later under res/xml/values-v19. -->
+<resources>
+    <!-- Dummy codeArrays for recents emoji keyboard.
+         Do not remove these keys, because they are used as a template. -->
+    <array
+        name="emoji_recents"
+        format="string"
+    >
+        <item>52</item>
+        <item>45</item>
+        <item>43</item>
+        <item>45</item>
+        <item>4E</item>
+        <item>54</item>
+    </array>
+    <array
+        name="emoji_nature"
+        format="string"
+    >
+        <!-- <item>1f415</item> -->
+        <item>1f436</item>
+        <item>1f429</item>
+        <!-- <item>1f408</item> -->
+        <item>1f431</item>
+        <!-- <item>1f400</item> -->
+        <!-- <item>1f401</item> -->
+        <item>1f42d</item>
+        <item>1f439</item>
+        <item>1f422</item>
+        <!-- <item>1f407</item> -->
+        <item>1f430</item>
+        <!-- <item>1f413</item> -->
+        <item>1f414</item>
+        <item>1f423</item>
+        <item>1f424</item>
+        <item>1f425</item>
+        <item>1f426</item>
+        <!-- <item>1f40f</item> -->
+        <item>1f411</item>
+        <!-- <item>1f410</item> -->
+        <item>1f43a</item>
+        <!-- <item>1f403</item> -->
+        <!-- <item>1f402</item> -->
+        <!-- <item>1f404</item> -->
+        <item>1f42e</item>
+        <item>1f434</item>
+        <item>1f417</item>
+        <!-- <item>1f416</item> -->
+        <item>1f437</item>
+        <item>1f43d</item>
+        <item>1f438</item>
+        <item>1f40d</item>
+        <item>1f43c</item>
+        <item>1f427</item>
+        <item>1f418</item>
+        <item>1f428</item>
+        <item>1f412</item>
+        <item>1f435</item>
+        <!-- <item>1f406</item> -->
+        <item>1f42f</item>
+        <item>1f43b</item>
+        <item>1f42b</item>
+        <!-- <item>1f42a</item> -->
+        <!-- <item>1f40a</item> -->
+        <item>1f433</item>
+        <!-- <item>1f40b</item> -->
+        <item>1f41f</item>
+        <item>1f420</item>
+        <item>1f421</item>
+        <item>1f419</item>
+        <item>1f41a</item>
+        <item>1f42c</item>
+        <item>1f40c</item>
+        <item>1f41b</item>
+        <item>1f41c</item>
+        <item>1f41d</item>
+        <item>1f41e</item>
+        <item>1f432</item>
+        <!-- <item>1f409</item> -->
+        <item>1f43e</item>
+        <item>1f378</item>
+        <item>1f37a</item>
+        <item>1f37b</item>
+        <item>1f377</item>
+        <item>1f379</item>
+        <item>1f376</item>
+        <!-- <item>2615</item> -->
+        <item>1f375</item>
+        <!-- <item>1f37c</item> -->
+        <item>1f374</item>
+        <item>1f368</item>
+        <item>1f367</item>
+        <item>1f366</item>
+        <item>1f369</item>
+        <item>1f370</item>
+        <item>1f36a</item>
+        <item>1f36b</item>
+        <item>1f36c</item>
+        <item>1f36d</item>
+        <item>1f36e</item>
+        <item>1f36f</item>
+        <item>1f373</item>
+        <item>1f354</item>
+        <item>1f35f</item>
+        <item>1f35d</item>
+        <item>1f355</item>
+        <item>1f356</item>
+        <item>1f357</item>
+        <item>1f364</item>
+        <item>1f363</item>
+        <item>1f371</item>
+        <item>1f35e</item>
+        <item>1f35c</item>
+        <item>1f359</item>
+        <item>1f35a</item>
+        <item>1f35b</item>
+        <item>1f372</item>
+        <item>1f365</item>
+        <item>1f362</item>
+        <item>1f361</item>
+        <item>1f358</item>
+        <item>1f360</item>
+        <item>1f34c</item>
+        <item>1f34e</item>
+        <item>1f34f</item>
+        <item>1f34a</item>
+        <!-- <item>1f34b</item> -->
+        <item>1f344</item>
+        <item>1f345</item>
+        <item>1f346</item>
+        <item>1f347</item>
+        <item>1f348</item>
+        <item>1f349</item>
+        <!-- <item>1f350</item> -->
+        <item>1f351</item>
+        <item>1f352</item>
+        <item>1f353</item>
+        <item>1f34d</item>
+        <item>1f330</item>
+        <item>1f331</item>
+        <!-- <item>1f332</item> -->
+        <!-- <item>1f333</item> -->
+        <item>1f334</item>
+        <item>1f335</item>
+        <item>1f337</item>
+        <item>1f338</item>
+        <item>1f339</item>
+        <item>1f340</item>
+        <item>1f341</item>
+        <item>1f342</item>
+        <item>1f343</item>
+        <item>1f33a</item>
+        <item>1f33b</item>
+        <item>1f33c</item>
+        <item>1f33d</item>
+        <item>1f33e</item>
+        <item>1f33f</item>
+        <item>2600</item>
+        <item>1f308</item>
+        <item>26c5</item>
+        <item>2601</item>
+        <item>1f301</item>
+        <item>1f302</item>
+        <!-- <item>2614</item> -->
+        <item>1f4a7</item>
+        <item>26a1</item>
+        <item>1f300</item>
+        <item>2744</item>
+        <item>26c4</item>
+        <item>1f319</item>
+        <!-- <item>1f31e</item> -->
+        <!-- <item>1f31d</item> -->
+        <!-- <item>1f31a</item> -->
+        <item>1f31b</item>
+        <!-- <item>1f31c</item> -->
+        <item>1f311</item>
+        <!-- <item>1f312</item> -->
+        <item>1f313</item>
+        <item>1f314</item>
+        <item>1f315</item>
+        <!-- <item>1f316</item> -->
+        <!-- <item>1f317</item> -->
+        <!-- <item>1f318</item> -->
+        <item>1f391</item>
+        <item>1f304</item>
+        <item>1f305</item>
+        <item>1f307</item>
+        <item>1f306</item>
+        <item>1f303</item>
+        <item>1f30c</item>
+        <item>1f309</item>
+        <item>1f30a</item>
+        <item>1f30b</item>
+        <!-- <item>1f30e</item> -->
+        <item>1f30f</item>
+        <!-- <item>1f30d</item> -->
+        <!-- <item>1f310</item> -->
+    </array>
+    <array
+        name="emoji_symbols"
+        format="string"
+    >
+        <!-- <item>fe82e|0031,20e3</item> -->
+        <!-- <item>fe82f|0032,20e3</item> -->
+        <!-- <item>fe830|0033,20e3</item> -->
+        <!-- <item>fe831|0034,20e3</item> -->
+        <!-- <item>fe832|0035,20e3</item> -->
+        <!-- <item>fe833|0036,20e3</item> -->
+        <!-- <item>fe834|0037,20e3</item> -->
+        <!-- <item>fe835|0038,20e3</item> -->
+        <!-- <item>fe836|0039,20e3</item> -->
+        <!-- <item>fe837|0030,20e3</item> -->
+        <!-- <item>1f51f</item> -->
+        <!-- <item>fe82c|0023,20e3</item> -->
+        <item>1f51d</item>
+        <item>1f519</item>
+        <item>1f51b</item>
+        <item>1f51c</item>
+        <item>1f51a</item>
+        <item>23f3</item>
+        <item>231b</item>
+        <item>23f0</item>
+        <item>2648</item>
+        <item>2649</item>
+        <item>264a</item>
+        <item>264b</item>
+        <item>264c</item>
+        <item>264d</item>
+        <item>264e</item>
+        <item>264f</item>
+        <item>2650</item>
+        <item>2651</item>
+        <item>2652</item>
+        <item>2653</item>
+        <item>26ce</item>
+        <item>1f531</item>
+        <item>1f52f</item>
+        <item>1f6bb</item>
+        <!-- <item>1f6ae</item> -->
+        <!-- <item>1f6af</item> -->
+        <!-- <item>1f6b0</item> -->
+        <!-- <item>1f6b1</item> -->
+        <item>1f170</item>
+        <item>1f171</item>
+        <item>1f18e</item>
+        <item>1f17e</item>
+        <item>1f4ae</item>
+        <item>1f4af</item>
+        <item>1f520</item>
+        <item>1f521</item>
+        <item>1f522</item>
+        <item>1f523</item>
+        <item>1f524</item>
+        <item>27bf</item>
+        <item>1f4f6</item>
+        <item>1f4f3</item>
+        <item>1f4f4</item>
+        <!-- <item>1f4f5</item> -->
+        <item>1f6b9</item>
+        <item>1f6ba</item>
+        <item>1f6bc</item>
+        <item>267f</item>
+        <item>267b</item>
+        <item>1f6ad</item>
+        <item>1f6a9</item>
+        <item>26a0</item>
+        <item>1f201</item>
+        <item>1f51e</item>
+        <item>26d4</item>
+        <item>1f192</item>
+        <item>1f197</item>
+        <item>1f195</item>
+        <item>1f198</item>
+        <item>1f199</item>
+        <item>1f193</item>
+        <item>1f196</item>
+        <item>1f19a</item>
+        <item>1f232</item>
+        <item>1f233</item>
+        <item>1f234</item>
+        <item>1f235</item>
+        <item>1f236</item>
+        <item>1f237</item>
+        <item>1f238</item>
+        <item>1f239</item>
+        <item>1f202</item>
+        <item>1f23a</item>
+        <item>1f250</item>
+        <item>1f251</item>
+        <item>3299</item>
+        <item>00ae</item>
+        <item>00a9</item>
+        <item>2122</item>
+        <item>1f21a</item>
+        <item>1f22f</item>
+        <item>3297</item>
+        <item>2b55</item>
+        <item>274c</item>
+        <item>274e</item>
+        <item>2139</item>
+        <item>1f6ab</item>
+        <item>2705</item>
+        <item>2714</item>
+        <item>1f517</item>
+        <item>2734</item>
+        <item>2733</item>
+        <item>2795</item>
+        <item>2796</item>
+        <item>2716</item>
+        <item>2797</item>
+        <item>1f4a0</item>
+        <item>1f4a1</item>
+        <item>1f4a4</item>
+        <item>1f4a2</item>
+        <item>1f525</item>
+        <item>1f4a5</item>
+        <item>1f4a8</item>
+        <item>1f4a6</item>
+        <item>1f4ab</item>
+        <item>1f55b</item>
+        <!-- <item>1f567</item> -->
+        <item>1f550</item>
+        <!-- <item>1f55c</item> -->
+        <item>1f551</item>
+        <!-- <item>1f55d</item> -->
+        <item>1f552</item>
+        <!-- <item>1f55e</item> -->
+        <item>1f553</item>
+        <!-- <item>1f55f</item> -->
+        <item>1f554</item>
+        <!-- <item>1f560</item> -->
+        <item>1f555</item>
+        <!-- <item>1f561</item> -->
+        <item>1f556</item>
+        <!-- <item>1f562</item> -->
+        <item>1f557</item>
+        <!-- <item>1f563</item> -->
+        <item>1f558</item>
+        <!-- <item>1f564</item> -->
+        <item>1f559</item>
+        <!-- <item>1f565</item> -->
+        <item>1f55a</item>
+        <!-- <item>1f566</item> -->
+        <item>2195</item>
+        <item>2b06</item>
+        <item>2197</item>
+        <item>27a1</item>
+        <item>2198</item>
+        <item>2b07</item>
+        <item>2199</item>
+        <item>2b05</item>
+        <item>2196</item>
+        <item>2194</item>
+        <item>2934</item>
+        <item>2935</item>
+        <item>23ea</item>
+        <item>23eb</item>
+        <item>23ec</item>
+        <item>23e9</item>
+        <item>25c0</item>
+        <item>25b6</item>
+        <item>1f53d</item>
+        <item>1f53c</item>
+        <item>2747</item>
+        <item>2728</item>
+        <item>1f534</item>
+        <item>1f535</item>
+        <item>26aa</item>
+        <item>26ab</item>
+        <item>1f533</item>
+        <item>1f532</item>
+        <item>2b50</item>
+        <item>1f31f</item>
+        <item>1f320</item>
+        <item>25ab</item>
+        <item>25aa</item>
+        <item>25fd</item>
+        <item>25fe</item>
+        <item>25fb</item>
+        <item>25fc</item>
+        <item>2b1c</item>
+        <item>2b1b</item>
+        <item>1f538</item>
+        <item>1f539</item>
+        <item>1f536</item>
+        <item>1f537</item>
+        <item>1f53a</item>
+        <item>1f53b</item>
+        <item>2754</item>
+        <item>2753</item>
+        <item>2755</item>
+        <item>2757</item>
+        <item>203c</item>
+        <item>2049</item>
+        <item>3030</item>
+        <item>27b0</item>
+        <item>2660</item>
+        <item>2665</item>
+        <item>2663</item>
+        <item>2666</item>
+        <item>1f194</item>
+        <item>1f511</item>
+        <item>21a9</item>
+        <item>1f191</item>
+        <item>1f50d</item>
+        <item>1f512</item>
+        <item>1f513</item>
+        <item>21aa</item>
+        <item>1f510</item>
+        <!-- <item>2611</item> -->
+        <item>1f518</item>
+        <item>1f50e</item>
+        <item>1f516</item>
+        <item>1f50f</item>
+        <item>1f503</item>
+        <!-- <item>1f500</item> -->
+        <!-- <item>1f501</item> -->
+        <!-- <item>1f502</item> -->
+        <!-- <item>1f504</item> -->
+        <item>1f4e7</item>
+        <!-- <item>1f505</item> -->
+        <!-- <item>1f506</item> -->
+        <!-- <item>1f507</item> -->
+        <!-- <item>1f508</item> -->
+        <!-- <item>1f509</item> -->
+        <item>1f50a</item>
+    </array>
+    <array
+        name="emoji_faces"
+        format="string"
+    >
+        <item>263a</item>
+        <item>1f60a</item>
+        <!-- <item>1f600</item> -->
+        <item>1f601</item>
+        <item>1f602</item>
+        <item>1f603</item>
+        <item>1f604</item>
+        <item>1f605</item>
+        <item>1f606</item>
+        <!-- <item>1f607</item> -->
+        <!-- <item>1f608</item> -->
+        <item>1f609</item>
+        <!-- <item>1f62f</item> -->
+        <!-- <item>1f610</item> -->
+        <!-- <item>1f611</item> -->
+        <!-- <item>1f615</item> -->
+        <item>1f620</item>
+        <!-- <item>1f62c</item> -->
+        <item>1f621</item>
+        <item>1f622</item>
+        <!-- <item>1f634</item> -->
+        <!-- <item>1f62e</item> -->
+        <item>1f623</item>
+        <item>1f624</item>
+        <item>1f625</item>
+        <!-- <item>1f626</item> -->
+        <!-- <item>1f627</item> -->
+        <item>1f628</item>
+        <item>1f629</item>
+        <item>1f630</item>
+        <!-- <item>1f61f</item> -->
+        <item>1f631</item>
+        <item>1f632</item>
+        <item>1f633</item>
+        <item>1f635</item>
+        <!-- <item>1f636</item> -->
+        <item>1f637</item>
+        <item>1f61e</item>
+        <item>1f612</item>
+        <item>1f60d</item>
+        <!-- <item>1f61b</item> -->
+        <item>1f61c</item>
+        <item>1f61d</item>
+        <item>1f60b</item>
+        <!-- <item>1f617</item> -->
+        <!-- <item>1f619</item> -->
+        <item>1f618</item>
+        <item>1f61a</item>
+        <!-- <item>1f60e</item> -->
+        <item>1f62d</item>
+        <item>1f60c</item>
+        <item>1f616</item>
+        <item>1f614</item>
+        <item>1f62a</item>
+        <item>1f60f</item>
+        <item>1f613</item>
+        <item>1f62b</item>
+        <item>1f64b</item>
+        <item>1f64c</item>
+        <item>1f64d</item>
+        <item>1f645</item>
+        <item>1f646</item>
+        <item>1f647</item>
+        <item>1f64e</item>
+        <item>1f64f</item>
+        <item>1f63a</item>
+        <item>1f63c</item>
+        <item>1f638</item>
+        <item>1f639</item>
+        <item>1f63b</item>
+        <item>1f63d</item>
+        <item>1f63f</item>
+        <item>1f63e</item>
+        <item>1f640</item>
+        <item>1f648</item>
+        <item>1f649</item>
+        <item>1f64a</item>
+        <item>1f4a9</item>
+        <item>1f476</item>
+        <item>1f466</item>
+        <item>1f467</item>
+        <item>1f468</item>
+        <item>1f469</item>
+        <item>1f474</item>
+        <item>1f475</item>
+        <item>1f48f</item>
+        <item>1f491</item>
+        <item>1f46a</item>
+        <item>1f46b</item>
+        <!-- <item>1f46c</item> -->
+        <!-- <item>1f46d</item> -->
+        <item>1f464</item>
+        <!-- <item>1f465</item> -->
+        <item>1f46e</item>
+        <item>1f477</item>
+        <item>1f481</item>
+        <item>1f482</item>
+        <item>1f46f</item>
+        <item>1f470</item>
+        <item>1f478</item>
+        <item>1f385</item>
+        <item>1f47c</item>
+        <!-- <item>1f471</item> -->
+        <!-- <item>1f472</item> -->
+        <!-- <item>1f473</item> -->
+        <item>1f483</item>
+        <item>1f486</item>
+        <item>1f487</item>
+        <item>1f485</item>
+        <item>1f47b</item>
+        <item>1f479</item>
+        <item>1f47a</item>
+        <item>1f47d</item>
+        <item>1f47e</item>
+        <item>1f47f</item>
+        <item>1f480</item>
+        <item>1f4aa</item>
+        <item>1f440</item>
+        <item>1f442</item>
+        <item>1f443</item>
+        <item>1f463</item>
+        <item>1f444</item>
+        <item>1f445</item>
+        <item>1f48b</item>
+        <item>2764</item>
+        <item>1f499</item>
+        <item>1f49a</item>
+        <item>1f49b</item>
+        <item>1f49c</item>
+        <item>1f493</item>
+        <item>1f494</item>
+        <item>1f495</item>
+        <item>1f496</item>
+        <item>1f497</item>
+        <item>1f498</item>
+        <item>1f49d</item>
+        <item>1f49e</item>
+        <item>1f49f</item>
+        <item>1f44d</item>
+        <item>1f44e</item>
+        <item>1f44c</item>
+        <item>270a</item>
+        <item>270c</item>
+        <item>270b</item>
+        <item>1f44a</item>
+        <!-- <item>261d</item> -->
+        <item>1f446</item>
+        <item>1f447</item>
+        <item>1f448</item>
+        <item>1f449</item>
+        <item>1f44b</item>
+        <item>1f44f</item>
+        <!-- <item>1f450</item> -->
+    </array>
+    <array
+        name="emoji_objects"
+        format="string"
+    >
+        <item>1f530</item>
+        <item>1f484</item>
+        <item>1f45e</item>
+        <item>1f45f</item>
+        <item>1f451</item>
+        <item>1f452</item>
+        <item>1f3a9</item>
+        <item>1f393</item>
+        <item>1f453</item>
+        <item>231a</item>
+        <item>1f454</item>
+        <item>1f455</item>
+        <item>1f456</item>
+        <item>1f457</item>
+        <item>1f458</item>
+        <item>1f459</item>
+        <item>1f460</item>
+        <item>1f461</item>
+        <item>1f462</item>
+        <item>1f45a</item>
+        <item>1f45c</item>
+        <item>1f4bc</item>
+        <item>1f392</item>
+        <item>1f45d</item>
+        <item>1f45b</item>
+        <item>1f4b0</item>
+        <item>1f4b3</item>
+        <item>1f4b2</item>
+        <item>1f4b5</item>
+        <item>1f4b4</item>
+        <!-- <item>1f4b6</item> -->
+        <!-- <item>1f4b7</item> -->
+        <item>1f4b8</item>
+        <item>1f4b1</item>
+        <item>1f4b9</item>
+        <item>1f52b</item>
+        <item>1f52a</item>
+        <item>1f4a3</item>
+        <item>1f489</item>
+        <item>1f48a</item>
+        <item>1f6ac</item>
+        <item>1f514</item>
+        <!-- <item>1f515</item> -->
+        <item>1f6aa</item>
+        <!-- <item>1f52c</item> -->
+        <!-- <item>1f52d</item> -->
+        <item>1f52e</item>
+        <item>1f526</item>
+        <item>1f50b</item>
+        <item>1f50c</item>
+        <item>1f4dc</item>
+        <item>1f4d7</item>
+        <item>1f4d8</item>
+        <item>1f4d9</item>
+        <item>1f4da</item>
+        <item>1f4d4</item>
+        <item>1f4d2</item>
+        <item>1f4d1</item>
+        <item>1f4d3</item>
+        <item>1f4d5</item>
+        <item>1f4d6</item>
+        <item>1f4f0</item>
+        <item>1f4db</item>
+        <item>1f383</item>
+        <item>1f384</item>
+        <item>1f380</item>
+        <item>1f381</item>
+        <item>1f382</item>
+        <item>1f388</item>
+        <item>1f386</item>
+        <item>1f387</item>
+        <item>1f389</item>
+        <item>1f38a</item>
+        <item>1f38d</item>
+        <item>1f38f</item>
+        <item>1f38c</item>
+        <item>1f390</item>
+        <item>1f38b</item>
+        <item>1f38e</item>
+        <item>1f4f1</item>
+        <item>1f4f2</item>
+        <item>1f4df</item>
+        <item>260e</item>
+        <item>1f4de</item>
+        <item>1f4e0</item>
+        <item>1f4e6</item>
+        <item>2709</item>
+        <item>1f4e8</item>
+        <item>1f4e9</item>
+        <item>1f4ea</item>
+        <item>1f4eb</item>
+        <!-- <item>1f4ed</item> -->
+        <!-- <item>1f4ec</item> -->
+        <item>1f4ee</item>
+        <item>1f4e4</item>
+        <item>1f4e5</item>
+        <!-- <item>1f4ef</item> -->
+        <item>1f4e2</item>
+        <item>1f4e3</item>
+        <item>1f4e1</item>
+        <item>1f4ac</item>
+        <!-- <item>1f4ad</item> -->
+        <item>2712</item>
+        <item>270f</item>
+        <item>1f4dd</item>
+        <item>1f4cf</item>
+        <item>1f4d0</item>
+        <item>1f4cd</item>
+        <item>1f4cc</item>
+        <item>1f4ce</item>
+        <item>2702</item>
+        <item>1f4ba</item>
+        <item>1f4bb</item>
+        <item>1f4bd</item>
+        <item>1f4be</item>
+        <item>1f4bf</item>
+        <item>1f4c6</item>
+        <item>1f4c5</item>
+        <item>1f4c7</item>
+        <item>1f4cb</item>
+        <item>1f4c1</item>
+        <item>1f4c2</item>
+        <item>1f4c3</item>
+        <item>1f4c4</item>
+        <item>1f4ca</item>
+        <item>1f4c8</item>
+        <item>1f4c9</item>
+        <item>26fa</item>
+        <item>1f3a1</item>
+        <item>1f3a2</item>
+        <item>1f3a0</item>
+        <item>1f3aa</item>
+        <item>1f3a8</item>
+        <item>1f3ac</item>
+        <item>1f3a5</item>
+        <item>1f4f7</item>
+        <item>1f4f9</item>
+        <item>1f3a6</item>
+        <item>1f3ad</item>
+        <item>1f3ab</item>
+        <item>1f3ae</item>
+        <item>1f3b2</item>
+        <item>1f3b0</item>
+        <item>1f0cf</item>
+        <item>1f3b4</item>
+        <item>1f004</item>
+        <item>1f3af</item>
+        <item>1f4fa</item>
+        <item>1f4fb</item>
+        <item>1f4c0</item>
+        <item>1f4fc</item>
+        <item>1f3a7</item>
+        <item>1f3a4</item>
+        <item>1f3b5</item>
+        <item>1f3b6</item>
+        <item>1f3bc</item>
+        <item>1f3bb</item>
+        <item>1f3b9</item>
+        <item>1f3b7</item>
+        <item>1f3ba</item>
+        <item>1f3b8</item>
+        <item>303d</item>
+    </array>
+    <array
+        name="emoji_places"
+        format="string"
+    >
+        <item>1f3e0</item>
+        <item>1f3e1</item>
+        <item>1f3e2</item>
+        <item>1f3e3</item>
+        <!-- <item>1f3e4</item> -->
+        <item>1f3e5</item>
+        <item>1f3e6</item>
+        <item>1f3e7</item>
+        <item>1f3e8</item>
+        <item>1f3e9</item>
+        <item>1f3ea</item>
+        <item>1f3eb</item>
+        <item>26ea</item>
+        <item>26f2</item>
+        <item>1f3ec</item>
+        <item>1f3ef</item>
+        <item>1f3f0</item>
+        <item>1f3ed</item>
+        <item>1f5fb</item>
+        <item>1f5fc</item>
+        <item>1f5fd</item>
+        <item>1f5fe</item>
+        <item>1f5ff</item>
+        <item>2693</item>
+        <item>1f3ee</item>
+        <item>1f488</item>
+        <item>1f527</item>
+        <item>1f528</item>
+        <item>1f529</item>
+        <!-- <item>1f6bf</item> -->
+        <!-- <item>1f6c1</item> -->
+        <item>1f6c0</item>
+        <item>1f6bd</item>
+        <item>1f6be</item>
+        <item>1f3bd</item>
+        <item>1f3a3</item>
+        <item>1f3b1</item>
+        <item>1f3b3</item>
+        <item>26be</item>
+        <item>26f3</item>
+        <item>1f3be</item>
+        <item>26bd</item>
+        <item>1f3bf</item>
+        <item>1f3c0</item>
+        <item>1f3c1</item>
+        <item>1f3c2</item>
+        <item>1f3c3</item>
+        <item>1f3c4</item>
+        <item>1f3c6</item>
+        <!-- <item>1f3c7</item> -->
+        <item>1f40e</item>
+        <item>1f3c8</item>
+        <!-- <item>1f3c9</item> -->
+        <item>1f3ca</item>
+        <!-- <item>1f682</item> -->
+        <item>1f683</item>
+        <item>1f684</item>
+        <item>1f685</item>
+        <!-- <item>1f686</item> -->
+        <item>1f687</item>
+        <item>24c2</item>
+        <!-- <item>1f688</item> -->
+        <!-- <item>1f68a</item> -->
+        <!-- <item>1f68b</item> -->
+        <item>1f68c</item>
+        <!-- <item>1f68d</item> -->
+        <!-- <item>1f68e</item> -->
+        <item>1f68f</item>
+        <!-- <item>1f690</item> -->
+        <item>1f691</item>
+        <item>1f692</item>
+        <item>1f693</item>
+        <!-- <item>1f694</item> -->
+        <item>1f695</item>
+        <!-- <item>1f696</item> -->
+        <item>1f697</item>
+        <!-- <item>1f698</item> -->
+        <item>1f699</item>
+        <!-- <item>1f69a</item> -->
+        <!-- <item>1f69b</item> -->
+        <!-- <item>1f69c</item> -->
+        <!-- <item>1f69d</item> -->
+        <!-- <item>1f69e</item> -->
+        <!-- <item>1f69f</item> -->
+        <!-- <item>1f6a0</item> -->
+        <!-- <item>1f6a1</item> -->
+        <item>1f6a2</item>
+        <!-- <item>1f6a3</item> -->
+        <!-- <item>1f681</item> -->
+        <item>2708</item>
+        <!-- <item>1f6c2</item> -->
+        <!-- <item>1f6c3</item> -->
+        <!-- <item>1f6c4</item> -->
+        <!-- <item>1f6c5</item> -->
+        <item>26f5</item>
+        <item>1f6b2</item>
+        <!-- <item>1f6b3</item> -->
+        <!-- <item>1f6b4</item> -->
+        <!-- <item>1f6b5</item> -->
+        <!-- <item>1f6b7</item> -->
+        <!-- <item>1f6b8</item> -->
+        <item>1f689</item>
+        <item>1f680</item>
+        <item>1f6a4</item>
+        <item>1f6b6</item>
+        <item>26fd</item>
+        <item>1f17f</item>
+        <item>1f6a5</item>
+        <!-- <item>1f6a6</item> -->
+        <item>1f6a7</item>
+        <item>1f6a8</item>
+        <item>2668</item>
+        <item>1f48c</item>
+        <item>1f48d</item>
+        <item>1f48e</item>
+        <item>1f490</item>
+        <item>1f492</item>
+        <item>fe4e5|1f1ef,1f1f5</item>
+        <item>fe4e6|1f1fa,1f1f8</item>
+        <item>fe4e7|1f1eb,1f1f7</item>
+        <item>fe4e8|1f1e9,1f1ea</item>
+        <item>fe4e9|1f1ee,1f1f9</item>
+        <item>fe4ea|1f1ec,1f1e7</item>
+        <item>fe4eb|1f1ea,1f1f8</item>
+        <item>fe4ec|1f1f7,1f1fa</item>
+        <item>fe4ed|1f1e8,1f1f3</item>
+        <item>fe4ee|1f1f0,1f1f7</item>
+    </array>
+    <array
+        name="emoji_emoticons"
+        format="string"
+    >
+        <item>=-O</item>
+        <item>:-P</item>
+        <item>;-)</item>
+        <item>:-(</item>
+        <item>:-)</item>
+        <item>:-!</item>
+        <item>:-$</item>
+        <item>B-)</item>
+        <item>:O</item>
+        <item>:-*</item>
+        <item>:-D</item>
+        <item>:\'(</item>
+        <item>:-\\</item>
+        <item>O:-)</item>
+        <item>:-[</item>
+    </array>
+</resources>
diff --git a/java/res/values-v19/emoji-categories.xml b/java/res/values-v19/emoji-categories.xml
new file mode 100644
index 0000000..a6affc4
--- /dev/null
+++ b/java/res/values-v19/emoji-categories.xml
@@ -0,0 +1,922 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     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.
+-->
+
+<!-- Note: This emoji code point list is valid on KLP and later (API >= 19).
+     There is another emoji code point list for JB-MR2 under res/xml/values and values-v18.-->
+<resources>
+    <!-- Dummy codeArrays for recents emoji keyboard.
+         Do not remove these keys, because they are used as a template. -->
+    <array
+        name="emoji_recents"
+        format="string"
+    >
+        <item>52</item>
+        <item>45</item>
+        <item>43</item>
+        <item>45</item>
+        <item>4E</item>
+        <item>54</item>
+    </array>
+    <array
+        name="emoji_nature"
+        format="string"
+    >
+        <item>1f415</item>
+        <item>1f436</item>
+        <item>1f429</item>
+        <item>1f408</item>
+        <item>1f431</item>
+        <item>1f400</item>
+        <item>1f401</item>
+        <item>1f42d</item>
+        <item>1f439</item>
+        <item>1f422</item>
+        <item>1f407</item>
+        <item>1f430</item>
+        <item>1f413</item>
+        <item>1f414</item>
+        <item>1f423</item>
+        <item>1f424</item>
+        <item>1f425</item>
+        <item>1f426</item>
+        <item>1f40f</item>
+        <item>1f411</item>
+        <item>1f410</item>
+        <item>1f43a</item>
+        <item>1f403</item>
+        <item>1f402</item>
+        <item>1f404</item>
+        <item>1f42e</item>
+        <item>1f434</item>
+        <item>1f417</item>
+        <item>1f416</item>
+        <item>1f437</item>
+        <item>1f43d</item>
+        <item>1f438</item>
+        <item>1f40d</item>
+        <item>1f43c</item>
+        <item>1f427</item>
+        <item>1f418</item>
+        <item>1f428</item>
+        <item>1f412</item>
+        <item>1f435</item>
+        <item>1f406</item>
+        <item>1f42f</item>
+        <item>1f43b</item>
+        <item>1f42b</item>
+        <item>1f42a</item>
+        <item>1f40a</item>
+        <item>1f433</item>
+        <item>1f40b</item>
+        <item>1f41f</item>
+        <item>1f420</item>
+        <item>1f421</item>
+        <item>1f419</item>
+        <item>1f41a</item>
+        <item>1f42c</item>
+        <item>1f40c</item>
+        <item>1f41b</item>
+        <item>1f41c</item>
+        <item>1f41d</item>
+        <item>1f41e</item>
+        <item>1f432</item>
+        <item>1f409</item>
+        <item>1f43e</item>
+        <item>1f378</item>
+        <item>1f37a</item>
+        <item>1f37b</item>
+        <item>1f377</item>
+        <item>1f379</item>
+        <item>1f376</item>
+        <item>2615</item>
+        <item>1f375</item>
+        <item>1f37c</item>
+        <item>1f374</item>
+        <item>1f368</item>
+        <item>1f367</item>
+        <item>1f366</item>
+        <item>1f369</item>
+        <item>1f370</item>
+        <item>1f36a</item>
+        <item>1f36b</item>
+        <item>1f36c</item>
+        <item>1f36d</item>
+        <item>1f36e</item>
+        <item>1f36f</item>
+        <item>1f373</item>
+        <item>1f354</item>
+        <item>1f35f</item>
+        <item>1f35d</item>
+        <item>1f355</item>
+        <item>1f356</item>
+        <item>1f357</item>
+        <item>1f364</item>
+        <item>1f363</item>
+        <item>1f371</item>
+        <item>1f35e</item>
+        <item>1f35c</item>
+        <item>1f359</item>
+        <item>1f35a</item>
+        <item>1f35b</item>
+        <item>1f372</item>
+        <item>1f365</item>
+        <item>1f362</item>
+        <item>1f361</item>
+        <item>1f358</item>
+        <item>1f360</item>
+        <item>1f34c</item>
+        <item>1f34e</item>
+        <item>1f34f</item>
+        <item>1f34a</item>
+        <item>1f34b</item>
+        <item>1f344</item>
+        <item>1f345</item>
+        <item>1f346</item>
+        <item>1f347</item>
+        <item>1f348</item>
+        <item>1f349</item>
+        <item>1f350</item>
+        <item>1f351</item>
+        <item>1f352</item>
+        <item>1f353</item>
+        <item>1f34d</item>
+        <item>1f330</item>
+        <item>1f331</item>
+        <item>1f332</item>
+        <item>1f333</item>
+        <item>1f334</item>
+        <item>1f335</item>
+        <item>1f337</item>
+        <item>1f338</item>
+        <item>1f339</item>
+        <item>1f340</item>
+        <item>1f341</item>
+        <item>1f342</item>
+        <item>1f343</item>
+        <item>1f33a</item>
+        <item>1f33b</item>
+        <item>1f33c</item>
+        <item>1f33d</item>
+        <item>1f33e</item>
+        <item>1f33f</item>
+        <item>2600</item>
+        <item>1f308</item>
+        <item>26c5</item>
+        <item>2601</item>
+        <item>1f301</item>
+        <item>1f302</item>
+        <item>2614</item>
+        <item>1f4a7</item>
+        <item>26a1</item>
+        <item>1f300</item>
+        <item>2744</item>
+        <item>26c4</item>
+        <item>1f319</item>
+        <item>1f31e</item>
+        <item>1f31d</item>
+        <item>1f31a</item>
+        <item>1f31b</item>
+        <item>1f31c</item>
+        <item>1f311</item>
+        <item>1f312</item>
+        <item>1f313</item>
+        <item>1f314</item>
+        <item>1f315</item>
+        <item>1f316</item>
+        <item>1f317</item>
+        <item>1f318</item>
+        <item>1f391</item>
+        <item>1f304</item>
+        <item>1f305</item>
+        <item>1f307</item>
+        <item>1f306</item>
+        <item>1f303</item>
+        <item>1f30c</item>
+        <item>1f309</item>
+        <item>1f30a</item>
+        <item>1f30b</item>
+        <item>1f30e</item>
+        <item>1f30f</item>
+        <item>1f30d</item>
+        <item>1f310</item>
+    </array>
+    <array
+        name="emoji_symbols"
+        format="string"
+    >
+        <item>fe82e|0031,20e3</item>
+        <item>fe82f|0032,20e3</item>
+        <item>fe830|0033,20e3</item>
+        <item>fe831|0034,20e3</item>
+        <item>fe832|0035,20e3</item>
+        <item>fe833|0036,20e3</item>
+        <item>fe834|0037,20e3</item>
+        <item>fe835|0038,20e3</item>
+        <item>fe836|0039,20e3</item>
+        <item>fe837|0030,20e3</item>
+        <item>1f51f</item>
+        <item>fe82c|0023,20e3</item>
+        <item>1f51d</item>
+        <item>1f519</item>
+        <item>1f51b</item>
+        <item>1f51c</item>
+        <item>1f51a</item>
+        <item>23f3</item>
+        <item>231b</item>
+        <item>23f0</item>
+        <item>2648</item>
+        <item>2649</item>
+        <item>264a</item>
+        <item>264b</item>
+        <item>264c</item>
+        <item>264d</item>
+        <item>264e</item>
+        <item>264f</item>
+        <item>2650</item>
+        <item>2651</item>
+        <item>2652</item>
+        <item>2653</item>
+        <item>26ce</item>
+        <item>1f531</item>
+        <item>1f52f</item>
+        <item>1f6bb</item>
+        <item>1f6ae</item>
+        <item>1f6af</item>
+        <item>1f6b0</item>
+        <item>1f6b1</item>
+        <item>1f170</item>
+        <item>1f171</item>
+        <item>1f18e</item>
+        <item>1f17e</item>
+        <item>1f4ae</item>
+        <item>1f4af</item>
+        <item>1f520</item>
+        <item>1f521</item>
+        <item>1f522</item>
+        <item>1f523</item>
+        <item>1f524</item>
+        <item>27bf</item>
+        <item>1f4f6</item>
+        <item>1f4f3</item>
+        <item>1f4f4</item>
+        <item>1f4f5</item>
+        <item>1f6b9</item>
+        <item>1f6ba</item>
+        <item>1f6bc</item>
+        <item>267f</item>
+        <item>267b</item>
+        <item>1f6ad</item>
+        <item>1f6a9</item>
+        <item>26a0</item>
+        <item>1f201</item>
+        <item>1f51e</item>
+        <item>26d4</item>
+        <item>1f192</item>
+        <item>1f197</item>
+        <item>1f195</item>
+        <item>1f198</item>
+        <item>1f199</item>
+        <item>1f193</item>
+        <item>1f196</item>
+        <item>1f19a</item>
+        <item>1f232</item>
+        <item>1f233</item>
+        <item>1f234</item>
+        <item>1f235</item>
+        <item>1f236</item>
+        <item>1f237</item>
+        <item>1f238</item>
+        <item>1f239</item>
+        <item>1f202</item>
+        <item>1f23a</item>
+        <item>1f250</item>
+        <item>1f251</item>
+        <item>3299</item>
+        <item>00ae</item>
+        <item>00a9</item>
+        <item>2122</item>
+        <item>1f21a</item>
+        <item>1f22f</item>
+        <item>3297</item>
+        <item>2b55</item>
+        <item>274c</item>
+        <item>274e</item>
+        <item>2139</item>
+        <item>1f6ab</item>
+        <item>2705</item>
+        <item>2714</item>
+        <item>1f517</item>
+        <item>2734</item>
+        <item>2733</item>
+        <item>2795</item>
+        <item>2796</item>
+        <item>2716</item>
+        <item>2797</item>
+        <item>1f4a0</item>
+        <item>1f4a1</item>
+        <item>1f4a4</item>
+        <item>1f4a2</item>
+        <item>1f525</item>
+        <item>1f4a5</item>
+        <item>1f4a8</item>
+        <item>1f4a6</item>
+        <item>1f4ab</item>
+        <item>1f55b</item>
+        <item>1f567</item>
+        <item>1f550</item>
+        <item>1f55c</item>
+        <item>1f551</item>
+        <item>1f55d</item>
+        <item>1f552</item>
+        <item>1f55e</item>
+        <item>1f553</item>
+        <item>1f55f</item>
+        <item>1f554</item>
+        <item>1f560</item>
+        <item>1f555</item>
+        <item>1f561</item>
+        <item>1f556</item>
+        <item>1f562</item>
+        <item>1f557</item>
+        <item>1f563</item>
+        <item>1f558</item>
+        <item>1f564</item>
+        <item>1f559</item>
+        <item>1f565</item>
+        <item>1f55a</item>
+        <item>1f566</item>
+        <item>2195</item>
+        <item>2b06</item>
+        <item>2197</item>
+        <item>27a1</item>
+        <item>2198</item>
+        <item>2b07</item>
+        <item>2199</item>
+        <item>2b05</item>
+        <item>2196</item>
+        <item>2194</item>
+        <item>2934</item>
+        <item>2935</item>
+        <item>23ea</item>
+        <item>23eb</item>
+        <item>23ec</item>
+        <item>23e9</item>
+        <item>25c0</item>
+        <item>25b6</item>
+        <item>1f53d</item>
+        <item>1f53c</item>
+        <item>2747</item>
+        <item>2728</item>
+        <item>1f534</item>
+        <item>1f535</item>
+        <item>26aa</item>
+        <item>26ab</item>
+        <item>1f533</item>
+        <item>1f532</item>
+        <item>2b50</item>
+        <item>1f31f</item>
+        <item>1f320</item>
+        <item>25ab</item>
+        <item>25aa</item>
+        <item>25fd</item>
+        <item>25fe</item>
+        <item>25fb</item>
+        <item>25fc</item>
+        <item>2b1c</item>
+        <item>2b1b</item>
+        <item>1f538</item>
+        <item>1f539</item>
+        <item>1f536</item>
+        <item>1f537</item>
+        <item>1f53a</item>
+        <item>1f53b</item>
+        <item>2754</item>
+        <item>2753</item>
+        <item>2755</item>
+        <item>2757</item>
+        <item>203c</item>
+        <item>2049</item>
+        <item>3030</item>
+        <item>27b0</item>
+        <item>2660</item>
+        <item>2665</item>
+        <item>2663</item>
+        <item>2666</item>
+        <item>1f194</item>
+        <item>1f511</item>
+        <item>21a9</item>
+        <item>1f191</item>
+        <item>1f50d</item>
+        <item>1f512</item>
+        <item>1f513</item>
+        <item>21aa</item>
+        <item>1f510</item>
+        <item>2611</item>
+        <item>1f518</item>
+        <item>1f50e</item>
+        <item>1f516</item>
+        <item>1f50f</item>
+        <item>1f503</item>
+        <item>1f500</item>
+        <item>1f501</item>
+        <item>1f502</item>
+        <item>1f504</item>
+        <item>1f4e7</item>
+        <item>1f505</item>
+        <item>1f506</item>
+        <item>1f507</item>
+        <item>1f508</item>
+        <item>1f509</item>
+        <item>1f50a</item>
+    </array>
+    <array
+        name="emoji_faces"
+        format="string"
+    >
+        <item>263a</item>
+        <item>1f60a</item>
+        <item>1f600</item>
+        <item>1f601</item>
+        <item>1f602</item>
+        <item>1f603</item>
+        <item>1f604</item>
+        <item>1f605</item>
+        <item>1f606</item>
+        <item>1f607</item>
+        <item>1f608</item>
+        <item>1f609</item>
+        <item>1f62f</item>
+        <item>1f610</item>
+        <item>1f611</item>
+        <item>1f615</item>
+        <item>1f620</item>
+        <item>1f62c</item>
+        <item>1f621</item>
+        <item>1f622</item>
+        <item>1f634</item>
+        <item>1f62e</item>
+        <item>1f623</item>
+        <item>1f624</item>
+        <item>1f625</item>
+        <item>1f626</item>
+        <item>1f627</item>
+        <item>1f628</item>
+        <item>1f629</item>
+        <item>1f630</item>
+        <item>1f61f</item>
+        <item>1f631</item>
+        <item>1f632</item>
+        <item>1f633</item>
+        <item>1f635</item>
+        <item>1f636</item>
+        <item>1f637</item>
+        <item>1f61e</item>
+        <item>1f612</item>
+        <item>1f60d</item>
+        <item>1f61b</item>
+        <item>1f61c</item>
+        <item>1f61d</item>
+        <item>1f60b</item>
+        <item>1f617</item>
+        <item>1f619</item>
+        <item>1f618</item>
+        <item>1f61a</item>
+        <item>1f60e</item>
+        <item>1f62d</item>
+        <item>1f60c</item>
+        <item>1f616</item>
+        <item>1f614</item>
+        <item>1f62a</item>
+        <item>1f60f</item>
+        <item>1f613</item>
+        <item>1f62b</item>
+        <item>1f64b</item>
+        <item>1f64c</item>
+        <item>1f64d</item>
+        <item>1f645</item>
+        <item>1f646</item>
+        <item>1f647</item>
+        <item>1f64e</item>
+        <item>1f64f</item>
+        <item>1f63a</item>
+        <item>1f63c</item>
+        <item>1f638</item>
+        <item>1f639</item>
+        <item>1f63b</item>
+        <item>1f63d</item>
+        <item>1f63f</item>
+        <item>1f63e</item>
+        <item>1f640</item>
+        <item>1f648</item>
+        <item>1f649</item>
+        <item>1f64a</item>
+        <item>1f4a9</item>
+        <item>1f476</item>
+        <item>1f466</item>
+        <item>1f467</item>
+        <item>1f468</item>
+        <item>1f469</item>
+        <item>1f474</item>
+        <item>1f475</item>
+        <item>1f48f</item>
+        <item>1f491</item>
+        <item>1f46a</item>
+        <item>1f46b</item>
+        <item>1f46c</item>
+        <item>1f46d</item>
+        <item>1f464</item>
+        <item>1f465</item>
+        <item>1f46e</item>
+        <item>1f477</item>
+        <item>1f481</item>
+        <item>1f482</item>
+        <item>1f46f</item>
+        <item>1f470</item>
+        <item>1f478</item>
+        <item>1f385</item>
+        <item>1f47c</item>
+        <item>1f471</item>
+        <item>1f472</item>
+        <item>1f473</item>
+        <item>1f483</item>
+        <item>1f486</item>
+        <item>1f487</item>
+        <item>1f485</item>
+        <item>1f47b</item>
+        <item>1f479</item>
+        <item>1f47a</item>
+        <item>1f47d</item>
+        <item>1f47e</item>
+        <item>1f47f</item>
+        <item>1f480</item>
+        <item>1f4aa</item>
+        <item>1f440</item>
+        <item>1f442</item>
+        <item>1f443</item>
+        <item>1f463</item>
+        <item>1f444</item>
+        <item>1f445</item>
+        <item>1f48b</item>
+        <item>2764</item>
+        <item>1f499</item>
+        <item>1f49a</item>
+        <item>1f49b</item>
+        <item>1f49c</item>
+        <item>1f493</item>
+        <item>1f494</item>
+        <item>1f495</item>
+        <item>1f496</item>
+        <item>1f497</item>
+        <item>1f498</item>
+        <item>1f49d</item>
+        <item>1f49e</item>
+        <item>1f49f</item>
+        <item>1f44d</item>
+        <item>1f44e</item>
+        <item>1f44c</item>
+        <item>270a</item>
+        <item>270c</item>
+        <item>270b</item>
+        <item>1f44a</item>
+        <item>261d</item>
+        <item>1f446</item>
+        <item>1f447</item>
+        <item>1f448</item>
+        <item>1f449</item>
+        <item>1f44b</item>
+        <item>1f44f</item>
+        <item>1f450</item>
+    </array>
+    <array
+        name="emoji_objects"
+        format="string"
+    >
+        <item>1f530</item>
+        <item>1f484</item>
+        <item>1f45e</item>
+        <item>1f45f</item>
+        <item>1f451</item>
+        <item>1f452</item>
+        <item>1f3a9</item>
+        <item>1f393</item>
+        <item>1f453</item>
+        <item>231a</item>
+        <item>1f454</item>
+        <item>1f455</item>
+        <item>1f456</item>
+        <item>1f457</item>
+        <item>1f458</item>
+        <item>1f459</item>
+        <item>1f460</item>
+        <item>1f461</item>
+        <item>1f462</item>
+        <item>1f45a</item>
+        <item>1f45c</item>
+        <item>1f4bc</item>
+        <item>1f392</item>
+        <item>1f45d</item>
+        <item>1f45b</item>
+        <item>1f4b0</item>
+        <item>1f4b3</item>
+        <item>1f4b2</item>
+        <item>1f4b5</item>
+        <item>1f4b4</item>
+        <item>1f4b6</item>
+        <item>1f4b7</item>
+        <item>1f4b8</item>
+        <item>1f4b1</item>
+        <item>1f4b9</item>
+        <item>1f52b</item>
+        <item>1f52a</item>
+        <item>1f4a3</item>
+        <item>1f489</item>
+        <item>1f48a</item>
+        <item>1f6ac</item>
+        <item>1f514</item>
+        <item>1f515</item>
+        <item>1f6aa</item>
+        <item>1f52c</item>
+        <item>1f52d</item>
+        <item>1f52e</item>
+        <item>1f526</item>
+        <item>1f50b</item>
+        <item>1f50c</item>
+        <item>1f4dc</item>
+        <item>1f4d7</item>
+        <item>1f4d8</item>
+        <item>1f4d9</item>
+        <item>1f4da</item>
+        <item>1f4d4</item>
+        <item>1f4d2</item>
+        <item>1f4d1</item>
+        <item>1f4d3</item>
+        <item>1f4d5</item>
+        <item>1f4d6</item>
+        <item>1f4f0</item>
+        <item>1f4db</item>
+        <item>1f383</item>
+        <item>1f384</item>
+        <item>1f380</item>
+        <item>1f381</item>
+        <item>1f382</item>
+        <item>1f388</item>
+        <item>1f386</item>
+        <item>1f387</item>
+        <item>1f389</item>
+        <item>1f38a</item>
+        <item>1f38d</item>
+        <item>1f38f</item>
+        <item>1f38c</item>
+        <item>1f390</item>
+        <item>1f38b</item>
+        <item>1f38e</item>
+        <item>1f4f1</item>
+        <item>1f4f2</item>
+        <item>1f4df</item>
+        <item>260e</item>
+        <item>1f4de</item>
+        <item>1f4e0</item>
+        <item>1f4e6</item>
+        <item>2709</item>
+        <item>1f4e8</item>
+        <item>1f4e9</item>
+        <item>1f4ea</item>
+        <item>1f4eb</item>
+        <item>1f4ed</item>
+        <item>1f4ec</item>
+        <item>1f4ee</item>
+        <item>1f4e4</item>
+        <item>1f4e5</item>
+        <item>1f4ef</item>
+        <item>1f4e2</item>
+        <item>1f4e3</item>
+        <item>1f4e1</item>
+        <item>1f4ac</item>
+        <item>1f4ad</item>
+        <item>2712</item>
+        <item>270f</item>
+        <item>1f4dd</item>
+        <item>1f4cf</item>
+        <item>1f4d0</item>
+        <item>1f4cd</item>
+        <item>1f4cc</item>
+        <item>1f4ce</item>
+        <item>2702</item>
+        <item>1f4ba</item>
+        <item>1f4bb</item>
+        <item>1f4bd</item>
+        <item>1f4be</item>
+        <item>1f4bf</item>
+        <item>1f4c6</item>
+        <item>1f4c5</item>
+        <item>1f4c7</item>
+        <item>1f4cb</item>
+        <item>1f4c1</item>
+        <item>1f4c2</item>
+        <item>1f4c3</item>
+        <item>1f4c4</item>
+        <item>1f4ca</item>
+        <item>1f4c8</item>
+        <item>1f4c9</item>
+        <item>26fa</item>
+        <item>1f3a1</item>
+        <item>1f3a2</item>
+        <item>1f3a0</item>
+        <item>1f3aa</item>
+        <item>1f3a8</item>
+        <item>1f3ac</item>
+        <item>1f3a5</item>
+        <item>1f4f7</item>
+        <item>1f4f9</item>
+        <item>1f3a6</item>
+        <item>1f3ad</item>
+        <item>1f3ab</item>
+        <item>1f3ae</item>
+        <item>1f3b2</item>
+        <item>1f3b0</item>
+        <item>1f0cf</item>
+        <item>1f3b4</item>
+        <item>1f004</item>
+        <item>1f3af</item>
+        <item>1f4fa</item>
+        <item>1f4fb</item>
+        <item>1f4c0</item>
+        <item>1f4fc</item>
+        <item>1f3a7</item>
+        <item>1f3a4</item>
+        <item>1f3b5</item>
+        <item>1f3b6</item>
+        <item>1f3bc</item>
+        <item>1f3bb</item>
+        <item>1f3b9</item>
+        <item>1f3b7</item>
+        <item>1f3ba</item>
+        <item>1f3b8</item>
+        <item>303d</item>
+    </array>
+    <array
+        name="emoji_places"
+        format="string"
+    >
+        <item>1f3e0</item>
+        <item>1f3e1</item>
+        <item>1f3e2</item>
+        <item>1f3e3</item>
+        <item>1f3e4</item>
+        <item>1f3e5</item>
+        <item>1f3e6</item>
+        <item>1f3e7</item>
+        <item>1f3e8</item>
+        <item>1f3e9</item>
+        <item>1f3ea</item>
+        <item>1f3eb</item>
+        <item>26ea</item>
+        <item>26f2</item>
+        <item>1f3ec</item>
+        <item>1f3ef</item>
+        <item>1f3f0</item>
+        <item>1f3ed</item>
+        <item>1f5fb</item>
+        <item>1f5fc</item>
+        <item>1f5fd</item>
+        <item>1f5fe</item>
+        <item>1f5ff</item>
+        <item>2693</item>
+        <item>1f3ee</item>
+        <item>1f488</item>
+        <item>1f527</item>
+        <item>1f528</item>
+        <item>1f529</item>
+        <item>1f6bf</item>
+        <item>1f6c1</item>
+        <item>1f6c0</item>
+        <item>1f6bd</item>
+        <item>1f6be</item>
+        <item>1f3bd</item>
+        <item>1f3a3</item>
+        <item>1f3b1</item>
+        <item>1f3b3</item>
+        <item>26be</item>
+        <item>26f3</item>
+        <item>1f3be</item>
+        <item>26bd</item>
+        <item>1f3bf</item>
+        <item>1f3c0</item>
+        <item>1f3c1</item>
+        <item>1f3c2</item>
+        <item>1f3c3</item>
+        <item>1f3c4</item>
+        <item>1f3c6</item>
+        <item>1f3c7</item>
+        <item>1f40e</item>
+        <item>1f3c8</item>
+        <item>1f3c9</item>
+        <item>1f3ca</item>
+        <item>1f682</item>
+        <item>1f683</item>
+        <item>1f684</item>
+        <item>1f685</item>
+        <item>1f686</item>
+        <item>1f687</item>
+        <item>24c2</item>
+        <item>1f688</item>
+        <item>1f68a</item>
+        <item>1f68b</item>
+        <item>1f68c</item>
+        <item>1f68d</item>
+        <item>1f68e</item>
+        <item>1f68f</item>
+        <item>1f690</item>
+        <item>1f691</item>
+        <item>1f692</item>
+        <item>1f693</item>
+        <item>1f694</item>
+        <item>1f695</item>
+        <item>1f696</item>
+        <item>1f697</item>
+        <item>1f698</item>
+        <item>1f699</item>
+        <item>1f69a</item>
+        <item>1f69b</item>
+        <item>1f69c</item>
+        <item>1f69d</item>
+        <item>1f69e</item>
+        <item>1f69f</item>
+        <item>1f6a0</item>
+        <item>1f6a1</item>
+        <item>1f6a2</item>
+        <item>1f6a3</item>
+        <item>1f681</item>
+        <item>2708</item>
+        <item>1f6c2</item>
+        <item>1f6c3</item>
+        <item>1f6c4</item>
+        <item>1f6c5</item>
+        <item>26f5</item>
+        <item>1f6b2</item>
+        <item>1f6b3</item>
+        <item>1f6b4</item>
+        <item>1f6b5</item>
+        <item>1f6b7</item>
+        <item>1f6b8</item>
+        <item>1f689</item>
+        <item>1f680</item>
+        <item>1f6a4</item>
+        <item>1f6b6</item>
+        <item>26fd</item>
+        <item>1f17f</item>
+        <item>1f6a5</item>
+        <item>1f6a6</item>
+        <item>1f6a7</item>
+        <item>1f6a8</item>
+        <item>2668</item>
+        <item>1f48c</item>
+        <item>1f48d</item>
+        <item>1f48e</item>
+        <item>1f490</item>
+        <item>1f492</item>
+        <item>fe4e5|1f1ef,1f1f5</item>
+        <item>fe4e6|1f1fa,1f1f8</item>
+        <item>fe4e7|1f1eb,1f1f7</item>
+        <item>fe4e8|1f1e9,1f1ea</item>
+        <item>fe4e9|1f1ee,1f1f9</item>
+        <item>fe4ea|1f1ec,1f1e7</item>
+        <item>fe4eb|1f1ea,1f1f8</item>
+        <item>fe4ec|1f1f7,1f1fa</item>
+        <item>fe4ed|1f1e8,1f1f3</item>
+        <item>fe4ee|1f1f0,1f1f7</item>
+    </array>
+    <array
+        name="emoji_emoticons"
+        format="string"
+    >
+        <item>=-O</item>
+        <item>:-P</item>
+        <item>;-)</item>
+        <item>:-(</item>
+        <item>:-)</item>
+        <item>:-!</item>
+        <item>:-$</item>
+        <item>B-)</item>
+        <item>:O</item>
+        <item>:-*</item>
+        <item>:-D</item>
+        <item>:\'(</item>
+        <item>:-\\</item>
+        <item>O:-)</item>
+        <item>:-[</item>
+    </array>
+</resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 3512686..4c50976 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -373,6 +373,13 @@
             <enum name="phone" value="7"  />
             <enum name="phoneSymbols" value="8"  />
             <enum name="number" value="9"  />
+            <enum name="emojiRecents" value="10" />
+            <enum name="emojiCategory1" value="11" />
+            <enum name="emojiCategory2" value="12" />
+            <enum name="emojiCategory3" value="13" />
+            <enum name="emojiCategory4" value="14" />
+            <enum name="emojiCategory5" value="15" />
+            <enum name="emojiCategory6" value="16" />
         </attr>
         <!-- This should be aligned with KeyboardId.MODE_* -->
         <attr name="mode" format="enum|string">
@@ -427,6 +434,13 @@
             <enum name="phone" value="7"  />
             <enum name="phoneSymbols" value="8"  />
             <enum name="number" value="9"  />
+            <enum name="emojiRecents" value="10" />
+            <enum name="emojiCategory1" value="11" />
+            <enum name="emojiCategory2" value="12" />
+            <enum name="emojiCategory3" value="13" />
+            <enum name="emojiCategory4" value="14" />
+            <enum name="emojiCategory5" value="15" />
+            <enum name="emojiCategory6" value="16" />
         </attr>
         <attr name="elementKeyboard" format="reference"/>
         <!-- Enable proximity characters correction. Disabled by default. -->
diff --git a/java/res/values/emoji-categories.xml b/java/res/values/emoji-categories.xml
new file mode 100644
index 0000000..734a7a7
--- /dev/null
+++ b/java/res/values/emoji-categories.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     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.
+-->
+
+<!-- Note: This emoji code point list is valid prior to JB-MR2 (API < 18).
+     There is another emoji code point list for JB-MR2 and KLP and later under
+     res/xml/values-v1[89].-->
+<resources>
+    <!-- Dummy codeArrays for recents emoji keyboard.
+         Do not remove these keys, because they are used as a template. -->
+    <array
+        name="emoji_recents"
+        format="string"
+    >
+        <item>52</item>
+        <item>45</item>
+        <item>43</item>
+        <item>45</item>
+        <item>4E</item>
+        <item>54</item>
+    </array>
+    <array
+        name="emoji_nature"
+        format="string"
+    >
+    </array>
+    <array
+        name="emoji_symbols"
+        format="string"
+    >
+    </array>
+    <array
+        name="emoji_faces"
+        format="string"
+    >
+    </array>
+    <array
+        name="emoji_objects"
+        format="string"
+    >
+    </array>
+    <array
+        name="emoji_places"
+        format="string"
+    >
+    </array>
+    <array
+        name="emoji_emoticons"
+        format="string"
+    >
+        <item>=-O</item>
+        <item>:-P</item>
+        <item>;-)</item>
+        <item>:-(</item>
+        <item>:-)</item>
+        <item>:-!</item>
+        <item>:-$</item>
+        <item>B-)</item>
+        <item>:O</item>
+        <item>:-*</item>
+        <item>:-D</item>
+        <item>:\'(</item>
+        <item>:-\\</item>
+        <item>O:-)</item>
+        <item>:-[</item>
+    </array>
+</resources>
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
index 8ce1263..7639432 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
@@ -157,7 +157,7 @@
 
             // Add the virtual children of the root View.
             final Keyboard keyboard = mKeyboardView.getKeyboard();
-            final Key[] keys = keyboard.mKeys;
+            final Key[] keys = keyboard.getKeys();
             for (Key key : keys) {
                 final int childVirtualViewId = generateVirtualViewIdForKey(key);
                 rootInfo.addChild(mKeyboardView, childVirtualViewId);
@@ -300,7 +300,7 @@
         }
         mVirtualViewIdToKey.clear();
 
-        final Key[] keys = keyboard.mKeys;
+        final Key[] keys = keyboard.getKeys();
         for (Key key : keys) {
             final int virtualViewId = generateVirtualViewIdForKey(key);
             mVirtualViewIdToKey.put(virtualViewId, key);
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index c40ddea..0b3737e 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -69,7 +69,7 @@
     public final int mMaxMoreKeysKeyboardColumn;
 
     /** Array of keys and icons in this keyboard */
-    public final Key[] mKeys;
+    private final Key[] mKeys;
     public final Key[] mShiftKeys;
     public final Key[] mAltCodeKeysWhileTyping;
     public final KeyboardIconsSet mIconsSet;
@@ -104,6 +104,28 @@
         mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
     }
 
+    protected Keyboard(final Keyboard keyboard) {
+        mId = keyboard.mId;
+        mThemeId = keyboard.mThemeId;
+        mOccupiedHeight = keyboard.mOccupiedHeight;
+        mOccupiedWidth = keyboard.mOccupiedWidth;
+        mMostCommonKeyHeight = keyboard.mMostCommonKeyHeight;
+        mMostCommonKeyWidth = keyboard.mMostCommonKeyWidth;
+        mMoreKeysTemplate = keyboard.mMoreKeysTemplate;
+        mMaxMoreKeysKeyboardColumn = keyboard.mMaxMoreKeysKeyboardColumn;
+        mKeyVisualAttributes = keyboard.mKeyVisualAttributes;
+        mTopPadding = keyboard.mTopPadding;
+        mVerticalGap = keyboard.mVerticalGap;
+
+        mKeys = keyboard.mKeys;
+        mShiftKeys = keyboard.mShiftKeys;
+        mAltCodeKeysWhileTyping = keyboard.mAltCodeKeysWhileTyping;
+        mIconsSet = keyboard.mIconsSet;
+
+        mProximityInfo = keyboard.mProximityInfo;
+        mProximityCharsCorrectionEnabled = keyboard.mProximityCharsCorrectionEnabled;
+    }
+
     public boolean hasProximityCharsCorrection(final int code) {
         if (!mProximityCharsCorrectionEnabled) {
             return false;
@@ -120,6 +142,10 @@
         return mProximityInfo;
     }
 
+    public Key[] getKeys() {
+        return mKeys;
+    }
+
     public Key getKey(final int code) {
         if (code == Constants.CODE_UNSPECIFIED) {
             return null;
@@ -130,7 +156,7 @@
                 return mKeyCache.valueAt(index);
             }
 
-            for (final Key key : mKeys) {
+            for (final Key key : getKeys()) {
                 if (key.getCode() == code) {
                     mKeyCache.put(code, key);
                     return key;
@@ -146,7 +172,7 @@
             return true;
         }
 
-        for (final Key key : mKeys) {
+        for (final Key key : getKeys()) {
             if (key == aKey) {
                 mKeyCache.put(key.getCode(), key);
                 return true;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 08ce064..53748bb 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -53,6 +53,13 @@
     public static final int ELEMENT_PHONE = 7;
     public static final int ELEMENT_PHONE_SYMBOLS = 8;
     public static final int ELEMENT_NUMBER = 9;
+    public static final int ELEMENT_EMOJI_RECENTS = 10;
+    public static final int ELEMENT_EMOJI_CATEGORY1 = 11;
+    public static final int ELEMENT_EMOJI_CATEGORY2 = 12;
+    public static final int ELEMENT_EMOJI_CATEGORY3 = 13;
+    public static final int ELEMENT_EMOJI_CATEGORY4 = 14;
+    public static final int ELEMENT_EMOJI_CATEGORY5 = 15;
+    public static final int ELEMENT_EMOJI_CATEGORY6 = 16;
 
     public final InputMethodSubtype mSubtype;
     public final Locale mLocale;
@@ -215,6 +222,13 @@
         case ELEMENT_PHONE: return "phone";
         case ELEMENT_PHONE_SYMBOLS: return "phoneSymbols";
         case ELEMENT_NUMBER: return "number";
+        case ELEMENT_EMOJI_RECENTS: return "emojiRecents";
+        case ELEMENT_EMOJI_CATEGORY1: return "emojiCategory1";
+        case ELEMENT_EMOJI_CATEGORY2: return "emojiCategory2";
+        case ELEMENT_EMOJI_CATEGORY3: return "emojiCategory3";
+        case ELEMENT_EMOJI_CATEGORY4: return "emojiCategory4";
+        case ELEMENT_EMOJI_CATEGORY5: return "emojiCategory5";
+        case ELEMENT_EMOJI_CATEGORY6: return "emojiCategory6";
         default: return null;
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index d049a86..0ef6802 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -285,7 +285,7 @@
         // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on.
         if (drawAllKeys || isHardwareAccelerated) {
             // Draw all keys.
-            for (final Key key : mKeyboard.mKeys) {
+            for (final Key key : mKeyboard.getKeys()) {
                 onDrawKey(key, canvas, paint);
             }
         } else {
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index a2001cb..6b76e24 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -39,7 +39,7 @@
 
         Key nearestKey = null;
         int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
-        for (final Key key : getKeyboard().mKeys) {
+        for (final Key key : getKeyboard().getKeys()) {
             final int dist = key.squaredDistanceToEdge(touchX, touchY);
             if (dist < nearestDist) {
                 nearestKey = key;
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 3725677..7124c4c 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -23,6 +23,7 @@
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.personalization.DynamicPersonalizationDictionaryWriter;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import java.io.File;
@@ -118,10 +119,9 @@
     }
 
     private static AbstractDictionaryWriter getDictionaryWriter(final Context context,
-            final String dictType, final boolean isUpdatable) {
-        if (isUpdatable) {
-            // TODO: Employ dynamically updatable DictionaryWriter.
-            return new DictionaryWriter(context, dictType);
+            final String dictType, final boolean isDynamicPersonalizationDictionary) {
+        if (isDynamicPersonalizationDictionary) {
+            return new DynamicPersonalizationDictionaryWriter(context, dictType);
         } else {
             return new DictionaryWriter(context, dictType);
         }
@@ -145,6 +145,7 @@
         mIsUpdatable = isUpdatable;
         mBinaryDictionary = null;
         mSharedDictionaryController = getSharedDictionaryController(filename);
+        // Currently, only dynamic personalization dictionary is updatable.
         mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index fc87cfa..491964f 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -327,7 +327,7 @@
         return (node == null) ? false : !node.mShortcutOnly;
     }
 
-    protected boolean removeBigram(final String word1, final String word2) {
+    public boolean removeBigram(final String word1, final String word2) {
         // Refer to addOrSetBigram() about word1.toLowerCase()
         final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
         final Node secondWord = searchWord(mRoots, word2, 0, null);
@@ -359,7 +359,7 @@
         return (node == null) ? -1 : node.mFrequency;
     }
 
-    protected NextWord getBigramWord(final String word1, final String word2) {
+    public NextWord getBigramWord(final String word1, final String word2) {
         // Refer to addOrSetBigram() about word1.toLowerCase()
         final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
         final Node secondWord = searchWord(mRoots, word2, 0, null);
@@ -700,7 +700,7 @@
         return null;
     }
 
-    protected void clearDictionary() {
+    public void clearDictionary() {
         mRoots = new NodeArray();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 8b1d60b..29f1146 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -19,7 +19,7 @@
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 
@@ -41,6 +41,7 @@
  *
  * TODO: Remove calls from classes except Ver3DictDecoder
  * TODO: Move this file to makedict/internal.
+ * TODO: Rename this class to DictDecoderUtils.
  */
 public final class BinaryDictDecoderUtils {
 
@@ -213,7 +214,7 @@
                     buffer[index++] = (byte)(0xFF & codePoint);
                 }
             }
-            buffer[index++] = FormatSpec.GROUP_CHARACTERS_TERMINATOR;
+            buffer[index++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
             return index - origin;
         }
 
@@ -237,7 +238,7 @@
                     buffer.write((byte) (0xFF & codePoint));
                 }
             }
-            buffer.write(FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+            buffer.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
         }
 
         /**
@@ -264,7 +265,7 @@
         static int readChar(final DictBuffer dictBuffer) {
             int character = dictBuffer.readUnsignedByte();
             if (!fitsOnOneByte(character)) {
-                if (FormatSpec.GROUP_CHARACTERS_TERMINATOR == character) {
+                if (FormatSpec.PTNODE_CHARACTERS_TERMINATOR == character) {
                     return FormatSpec.INVALID_CHARACTER;
                 }
                 character <<= 16;
@@ -295,14 +296,14 @@
             }
         }
         int address;
-        switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
+        switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
                 return dictBuffer.readUnsignedByte();
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
                 return dictBuffer.readUnsignedShort();
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
                 return dictBuffer.readUnsignedInt24();
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
             default:
                 return FormatSpec.NO_CHILDREN_ADDRESS;
         }
@@ -320,14 +321,14 @@
     }
 
     /**
-     * Reads and returns the char group count out of a buffer and forwards the pointer.
+     * Reads and returns the PtNode count out of a buffer and forwards the pointer.
      */
-    public static int readCharGroupCount(final DictBuffer dictBuffer) {
+    /* package */ static int readPtNodeCount(final DictBuffer dictBuffer) {
         final int msb = dictBuffer.readUnsignedByte();
-        if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= msb) {
+        if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= msb) {
             return msb;
         } else {
-            return ((FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT & msb) << 8)
+            return ((FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT & msb) << 8)
                     + dictBuffer.readUnsignedByte();
         }
     }
@@ -369,18 +370,18 @@
         final StringBuilder builder = new StringBuilder();
         // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH
         for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) {
-            CharGroupInfo currentInfo;
+            PtNodeInfo currentInfo;
             int loopCounter = 0;
             do {
                 dictBuffer.position(currentPos);
                 currentInfo = dictDecoder.readPtNode(currentPos, options);
-                if (BinaryDictIOUtils.isMovedGroup(currentInfo.mFlags, options)) {
+                if (BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, options)) {
                     currentPos = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
                 }
                 if (DBG && loopCounter++ > MAX_JUMPS) {
                     MakedictLog.d("Too many jumps - probably a bug");
                 }
-            } while (BinaryDictIOUtils.isMovedGroup(currentInfo.mFlags, options));
+            } while (BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, options));
             if (Integer.MIN_VALUE == frequency) frequency = currentInfo.mFrequency;
             builder.insert(0,
                     new String(currentInfo.mCharacters, 0, currentInfo.mCharacters.length));
@@ -395,14 +396,14 @@
             final FormatOptions options) {
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
         dictBuffer.position(headerSize);
-        final int count = readCharGroupCount(dictBuffer);
-        int groupPos = headerSize + BinaryDictIOUtils.getGroupCountSize(count);
+        final int count = readPtNodeCount(dictBuffer);
+        int groupPos = headerSize + BinaryDictIOUtils.getPtNodeCountSize(count);
         final StringBuilder builder = new StringBuilder();
         WeightedString result = null;
 
-        CharGroupInfo last = null;
+        PtNodeInfo last = null;
         for (int i = count - 1; i >= 0; --i) {
-            CharGroupInfo info = dictDecoder.readPtNode(groupPos, options);
+            PtNodeInfo info = dictDecoder.readPtNode(groupPos, options);
             groupPos = info.mEndAddress;
             if (info.mOriginalAddress == pos) {
                 builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
@@ -414,8 +415,8 @@
                     if (null == last) continue;
                     builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
                     dictBuffer.position(last.mChildrenAddress);
-                    i = readCharGroupCount(dictBuffer);
-                    groupPos = last.mChildrenAddress + BinaryDictIOUtils.getGroupCountSize(i);
+                    i = readPtNodeCount(dictBuffer);
+                    groupPos = last.mChildrenAddress + BinaryDictIOUtils.getPtNodeCountSize(i);
                     last = null;
                     continue;
                 }
@@ -424,8 +425,8 @@
             if (0 == i && BinaryDictIOUtils.hasChildrenAddress(last.mChildrenAddress)) {
                 builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
                 dictBuffer.position(last.mChildrenAddress);
-                i = readCharGroupCount(dictBuffer);
-                groupPos = last.mChildrenAddress + BinaryDictIOUtils.getGroupCountSize(i);
+                i = readPtNodeCount(dictBuffer);
+                groupPos = last.mChildrenAddress + BinaryDictIOUtils.getPtNodeCountSize(i);
                 last = null;
                 continue;
             }
@@ -444,25 +445,25 @@
      * @param dictDecoder the dict decoder, correctly positioned at the start of a node array.
      * @param headerSize the size, in bytes, of the file header.
      * @param reverseNodeArrayMap a mapping from addresses to already read node arrays.
-     * @param reverseGroupMap a mapping from addresses to already read character groups.
+     * @param reversePtNodeMap a mapping from addresses to already read PtNodes.
      * @param options file format options.
      * @return the read node array with all his children already read.
      */
     private static PtNodeArray readNodeArray(final Ver3DictDecoder dictDecoder,
             final int headerSize, final Map<Integer, PtNodeArray> reverseNodeArrayMap,
-            final Map<Integer, CharGroup> reverseGroupMap, final FormatOptions options)
+            final Map<Integer, PtNode> reversePtNodeMap, final FormatOptions options)
             throws IOException {
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
-        final ArrayList<CharGroup> nodeArrayContents = new ArrayList<CharGroup>();
+        final ArrayList<PtNode> nodeArrayContents = new ArrayList<PtNode>();
         final int nodeArrayOriginPos = dictBuffer.position();
 
         do { // Scan the linked-list node.
             final int nodeArrayHeadPos = dictBuffer.position();
-            final int count = readCharGroupCount(dictBuffer);
-            int groupOffsetPos = nodeArrayHeadPos + BinaryDictIOUtils.getGroupCountSize(count);
-            for (int i = count; i > 0; --i) { // Scan the array of CharGroup.
-                CharGroupInfo info = dictDecoder.readPtNode(groupOffsetPos, options);
-                if (BinaryDictIOUtils.isMovedGroup(info.mFlags, options)) continue;
+            final int count = readPtNodeCount(dictBuffer);
+            int groupOffsetPos = nodeArrayHeadPos + BinaryDictIOUtils.getPtNodeCountSize(count);
+            for (int i = count; i > 0; --i) { // Scan the array of PtNode.
+                PtNodeInfo info = dictDecoder.readPtNode(groupOffsetPos, options);
+                if (BinaryDictIOUtils.isMovedPtNode(info.mFlags, options)) continue;
                 ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
                 ArrayList<WeightedString> bigrams = null;
                 if (null != info.mBigrams) {
@@ -482,17 +483,17 @@
                         final int currentPosition = dictBuffer.position();
                         dictBuffer.position(info.mChildrenAddress);
                         children = readNodeArray(dictDecoder, headerSize, reverseNodeArrayMap,
-                                reverseGroupMap, options);
+                                reversePtNodeMap, options);
                         dictBuffer.position(currentPosition);
                     }
                     nodeArrayContents.add(
-                            new CharGroup(info.mCharacters, shortcutTargets, bigrams,
+                            new PtNode(info.mCharacters, shortcutTargets, bigrams,
                                     info.mFrequency,
                                     0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
                                     0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED), children));
                 } else {
                     nodeArrayContents.add(
-                            new CharGroup(info.mCharacters, shortcutTargets, bigrams,
+                            new PtNode(info.mCharacters, shortcutTargets, bigrams,
                                     info.mFrequency,
                                     0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
                                     0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED)));
@@ -566,9 +567,9 @@
         final FileHeader fileHeader = dictDecoder.readHeader();
 
         Map<Integer, PtNodeArray> reverseNodeArrayMapping = new TreeMap<Integer, PtNodeArray>();
-        Map<Integer, CharGroup> reverseGroupMapping = new TreeMap<Integer, CharGroup>();
+        Map<Integer, PtNode> reversePtNodeMapping = new TreeMap<Integer, PtNode>();
         final PtNodeArray root = readNodeArray(dictDecoder, fileHeader.mHeaderSize,
-                reverseNodeArrayMapping, reverseGroupMapping, fileHeader.mFormatOptions);
+                reverseNodeArrayMapping, reversePtNodeMapping, fileHeader.mFormatOptions);
 
         FusionDictionary newDict = new FusionDictionary(root, fileHeader.mDictionaryOptions);
         if (null != dict) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index 40effb5..b95db21 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -18,7 +18,7 @@
 
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
@@ -33,6 +33,8 @@
  * Encodes binary files for a FusionDictionary.
  *
  * All the methods in this class are static.
+ *
+ * TODO: Rename this class to DictEncoderUtils.
  */
 public class BinaryDictEncoderUtils {
 
@@ -58,46 +60,46 @@
      * @param characters the character array
      * @return the size of the char array, including the terminator if any
      */
-    static int getGroupCharactersSize(final int[] characters) {
+    static int getPtNodeCharactersSize(final int[] characters) {
         int size = CharEncoding.getCharArraySize(characters);
-        if (characters.length > 1) size += FormatSpec.GROUP_TERMINATOR_SIZE;
+        if (characters.length > 1) size += FormatSpec.PTNODE_TERMINATOR_SIZE;
         return size;
     }
 
     /**
-     * Compute the binary size of the character array in a group
+     * Compute the binary size of the character array in a PtNode
      *
      * If only one character, this is the size of this character. If many, it's the sum of their
      * sizes + 1 byte for the terminator.
      *
-     * @param group the group
+     * @param ptNode the PtNode
      * @return the size of the char array, including the terminator if any
      */
-    private static int getGroupCharactersSize(final CharGroup group) {
-        return getGroupCharactersSize(group.mChars);
+    private static int getPtNodeCharactersSize(final PtNode ptNode) {
+        return getPtNodeCharactersSize(ptNode.mChars);
     }
 
     /**
-     * Compute the binary size of the group count for a node array.
+     * Compute the binary size of the PtNode count for a node array.
      * @param nodeArray the nodeArray
-     * @return the size of the group count, either 1 or 2 bytes.
+     * @return the size of the PtNode count, either 1 or 2 bytes.
      */
-    private static int getGroupCountSize(final PtNodeArray nodeArray) {
-        return BinaryDictIOUtils.getGroupCountSize(nodeArray.mData.size());
+    private static int getPtNodeCountSize(final PtNodeArray nodeArray) {
+        return BinaryDictIOUtils.getPtNodeCountSize(nodeArray.mData.size());
     }
 
     /**
      * Compute the size of a shortcut in bytes.
      */
     private static int getShortcutSize(final WeightedString shortcut) {
-        int size = FormatSpec.GROUP_ATTRIBUTE_FLAGS_SIZE;
+        int size = FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
         final String word = shortcut.mWord;
         final int length = word.length();
         for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
             final int codePoint = word.codePointAt(i);
             size += CharEncoding.getCharSize(codePoint);
         }
-        size += FormatSpec.GROUP_TERMINATOR_SIZE;
+        size += FormatSpec.PTNODE_TERMINATOR_SIZE;
         return size;
     }
 
@@ -109,7 +111,7 @@
      */
     static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
         if (null == shortcutList || shortcutList.isEmpty()) return 0;
-        int size = FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
+        int size = FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
         for (final WeightedString shortcut : shortcutList) {
             size += getShortcutSize(shortcut);
         }
@@ -117,60 +119,60 @@
     }
 
     /**
-     * Compute the maximum size of a CharGroup, assuming 3-byte addresses for everything.
+     * Compute the maximum size of a PtNode, assuming 3-byte addresses for everything.
      *
-     * @param group the CharGroup to compute the size of.
+     * @param ptNode the PtNode to compute the size of.
      * @param options file format options.
-     * @return the maximum size of the group.
+     * @return the maximum size of the PtNode.
      */
-    private static int getCharGroupMaximumSize(final CharGroup group, final FormatOptions options) {
-        int size = getGroupHeaderSize(group, options);
+    private static int getPtNodeMaximumSize(final PtNode ptNode, final FormatOptions options) {
+        int size = getNodeHeaderSize(ptNode, options);
         // If terminal, one byte for the frequency
-        if (group.isTerminal()) size += FormatSpec.GROUP_FREQUENCY_SIZE;
-        size += FormatSpec.GROUP_MAX_ADDRESS_SIZE; // For children address
-        size += getShortcutListSize(group.mShortcutTargets);
-        if (null != group.mBigrams) {
-            size += (FormatSpec.GROUP_ATTRIBUTE_FLAGS_SIZE
-                    + FormatSpec.GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE)
-                    * group.mBigrams.size();
+        if (ptNode.isTerminal()) size += FormatSpec.PTNODE_FREQUENCY_SIZE;
+        size += FormatSpec.PTNODE_MAX_ADDRESS_SIZE; // For children address
+        size += getShortcutListSize(ptNode.mShortcutTargets);
+        if (null != ptNode.mBigrams) {
+            size += (FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE
+                    + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE)
+                    * ptNode.mBigrams.size();
         }
         return size;
     }
 
     /**
-     * Compute the maximum size of each node of a node array, assuming 3-byte addresses for
+     * Compute the maximum size of each PtNode of a PtNode array, assuming 3-byte addresses for
      * everything, and caches it in the `mCachedSize' member of the nodes; deduce the size of
      * the containing node array, and cache it it its 'mCachedSize' member.
      *
-     * @param nodeArray the node array to compute the maximum size of.
+     * @param ptNodeArray the node array to compute the maximum size of.
      * @param options file format options.
      */
-    private static void calculateNodeArrayMaximumSize(final PtNodeArray nodeArray,
+    private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray,
             final FormatOptions options) {
-        int size = getGroupCountSize(nodeArray);
-        for (CharGroup g : nodeArray.mData) {
-            final int groupSize = getCharGroupMaximumSize(g, options);
-            g.mCachedSize = groupSize;
-            size += groupSize;
+        int size = getPtNodeCountSize(ptNodeArray);
+        for (PtNode node : ptNodeArray.mData) {
+            final int nodeSize = getPtNodeMaximumSize(node, options);
+            node.mCachedSize = nodeSize;
+            size += nodeSize;
         }
         if (options.mSupportsDynamicUpdate) {
             size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
         }
-        nodeArray.mCachedSize = size;
+        ptNodeArray.mCachedSize = size;
     }
 
     /**
-     * Compute the size of the header (flag + [parent address] + characters size) of a CharGroup.
+     * Compute the size of the header (flag + [parent address] + characters size) of a PtNode.
      *
-     * @param group the group of which to compute the size of the header
+     * @param ptNode the PtNode of which to compute the size of the header
      * @param options file format options.
      */
-    private static int getGroupHeaderSize(final CharGroup group, final FormatOptions options) {
+    private static int getNodeHeaderSize(final PtNode ptNode, final FormatOptions options) {
         if (BinaryDictIOUtils.supportsDynamicUpdate(options)) {
-            return FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
-                    + getGroupCharactersSize(group);
+            return FormatSpec.PTNODE_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
+                    + getPtNodeCharactersSize(ptNode);
         } else {
-            return FormatSpec.GROUP_FLAGS_SIZE + getGroupCharactersSize(group);
+            return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode);
         }
     }
 
@@ -203,14 +205,14 @@
     // cache performance and dictionary size.
     /* package for tests */ static ArrayList<PtNodeArray> flattenTree(
             final PtNodeArray rootNodeArray) {
-        final int treeSize = FusionDictionary.countCharGroups(rootNodeArray);
+        final int treeSize = FusionDictionary.countPtNodes(rootNodeArray);
         MakedictLog.i("Counted nodes : " + treeSize);
         final ArrayList<PtNodeArray> flatTree = new ArrayList<PtNodeArray>(treeSize);
         return flattenTreeInner(flatTree, rootNodeArray);
     }
 
     private static ArrayList<PtNodeArray> flattenTreeInner(final ArrayList<PtNodeArray> list,
-            final PtNodeArray nodeArray) {
+            final PtNodeArray ptNodeArray) {
         // Removing the node is necessary if the tails are merged, because we would then
         // add the same node several times when we only want it once. A number of places in
         // the code also depends on any node being only once in the list.
@@ -228,11 +230,11 @@
         // this simple list.remove operation O(n*n) overall. On Android this overhead is very
         // high.
         // For future reference, the code to remove duplicate is a simple : list.remove(node);
-        list.add(nodeArray);
-        final ArrayList<CharGroup> branches = nodeArray.mData;
+        list.add(ptNodeArray);
+        final ArrayList<PtNode> branches = ptNodeArray.mData;
         final int nodeSize = branches.size();
-        for (CharGroup group : branches) {
-            if (null != group.mChildren) flattenTreeInner(list, group.mChildren);
+        for (PtNode ptNode : branches) {
+            if (null != ptNode.mChildren) flattenTreeInner(list, ptNode.mChildren);
         }
         return list;
     }
@@ -248,7 +250,7 @@
      * from the new position in the current node array to the new position in the target node
      * array.
      *
-     * @param currentNodeArray node array containing the CharGroup where the offset will be written
+     * @param currentNodeArray node array containing the PtNode where the offset will be written
      * @param offsetFromStartOfCurrentNodeArray offset, in bytes, from the start of currentNodeArray
      * @param targetNodeArray the target node array to get the offset to
      * @return the offset to the target node array
@@ -269,20 +271,20 @@
     }
 
     /**
-     * Get the offset from a position inside a current node array to a target CharGroup, during
+     * Get the offset from a position inside a current node array to a target PtNode, during
      * update.
      *
-     * @param currentNodeArray node array containing the CharGroup where the offset will be written
+     * @param currentNodeArray node array containing the PtNode where the offset will be written
      * @param offsetFromStartOfCurrentNodeArray offset, in bytes, from the start of currentNodeArray
-     * @param targetCharGroup the target CharGroup to get the offset to
-     * @return the offset to the target CharGroup
+     * @param targetPtNode the target PtNode to get the offset to
+     * @return the offset to the target PtNode
      */
     // TODO: is there any way to factorize this method with the one above?
-    private static int getOffsetToTargetCharGroupDuringUpdate(final PtNodeArray currentNodeArray,
-            final int offsetFromStartOfCurrentNodeArray, final CharGroup targetCharGroup) {
+    private static int getOffsetToTargetPtNodeDuringUpdate(final PtNodeArray currentNodeArray,
+            final int offsetFromStartOfCurrentNodeArray, final PtNode targetPtNode) {
         final int oldOffsetBasePoint = currentNodeArray.mCachedAddressBeforeUpdate
                 + offsetFromStartOfCurrentNodeArray;
-        final boolean isTargetBeforeCurrent = (targetCharGroup.mCachedAddressBeforeUpdate
+        final boolean isTargetBeforeCurrent = (targetPtNode.mCachedAddressBeforeUpdate
                 < oldOffsetBasePoint);
         // If the target is before the current node array, then its address has already been
         // updated. We can use the AfterUpdate member, and compare it to our own member after
@@ -292,9 +294,9 @@
         if (isTargetBeforeCurrent) {
             final int newOffsetBasePoint = currentNodeArray.mCachedAddressAfterUpdate
                     + offsetFromStartOfCurrentNodeArray;
-            return targetCharGroup.mCachedAddressAfterUpdate - newOffsetBasePoint;
+            return targetPtNode.mCachedAddressAfterUpdate - newOffsetBasePoint;
         } else {
-            return targetCharGroup.mCachedAddressBeforeUpdate - oldOffsetBasePoint;
+            return targetPtNode.mCachedAddressBeforeUpdate - oldOffsetBasePoint;
         }
     }
 
@@ -308,49 +310,49 @@
      * contents (as in, any of the addresses stored in the cache fields) have changed with
      * respect to their previous value.
      *
-     * @param nodeArray the node array to compute the size of.
+     * @param ptNodeArray the node array to compute the size of.
      * @param dict the dictionary in which the word/attributes are to be found.
      * @param formatOptions file format options.
      * @return false if none of the cached addresses inside the node array changed, true otherwise.
      */
-    private static boolean computeActualNodeArraySize(final PtNodeArray nodeArray,
+    private static boolean computeActualPtNodeArraySize(final PtNodeArray ptNodeArray,
             final FusionDictionary dict, final FormatOptions formatOptions) {
         boolean changed = false;
-        int size = getGroupCountSize(nodeArray);
-        for (CharGroup group : nodeArray.mData) {
-            group.mCachedAddressAfterUpdate = nodeArray.mCachedAddressAfterUpdate + size;
-            if (group.mCachedAddressAfterUpdate != group.mCachedAddressBeforeUpdate) {
+        int size = getPtNodeCountSize(ptNodeArray);
+        for (PtNode ptNode : ptNodeArray.mData) {
+            ptNode.mCachedAddressAfterUpdate = ptNodeArray.mCachedAddressAfterUpdate + size;
+            if (ptNode.mCachedAddressAfterUpdate != ptNode.mCachedAddressBeforeUpdate) {
                 changed = true;
             }
-            int groupSize = getGroupHeaderSize(group, formatOptions);
-            if (group.isTerminal()) groupSize += FormatSpec.GROUP_FREQUENCY_SIZE;
-            if (null == group.mChildren && formatOptions.mSupportsDynamicUpdate) {
-                groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
-            } else if (null != group.mChildren) {
+            int nodeSize = getNodeHeaderSize(ptNode, formatOptions);
+            if (ptNode.isTerminal()) nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE;
+            if (null == ptNode.mChildren && formatOptions.mSupportsDynamicUpdate) {
+                nodeSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+            } else if (null != ptNode.mChildren) {
                 if (formatOptions.mSupportsDynamicUpdate) {
-                    groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+                    nodeSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
                 } else {
-                    groupSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(nodeArray,
-                            groupSize + size, group.mChildren));
+                    nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray,
+                            nodeSize + size, ptNode.mChildren));
                 }
             }
-            groupSize += getShortcutListSize(group.mShortcutTargets);
-            if (null != group.mBigrams) {
-                for (WeightedString bigram : group.mBigrams) {
-                    final int offset = getOffsetToTargetCharGroupDuringUpdate(nodeArray,
-                            groupSize + size + FormatSpec.GROUP_FLAGS_SIZE,
+            nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
+            if (null != ptNode.mBigrams) {
+                for (WeightedString bigram : ptNode.mBigrams) {
+                    final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray,
+                            nodeSize + size + FormatSpec.PTNODE_FLAGS_SIZE,
                             FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord));
-                    groupSize += getByteSize(offset) + FormatSpec.GROUP_FLAGS_SIZE;
+                    nodeSize += getByteSize(offset) + FormatSpec.PTNODE_FLAGS_SIZE;
                 }
             }
-            group.mCachedSize = groupSize;
-            size += groupSize;
+            ptNode.mCachedSize = nodeSize;
+            size += nodeSize;
         }
         if (formatOptions.mSupportsDynamicUpdate) {
             size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
         }
-        if (nodeArray.mCachedSize != size) {
-            nodeArray.mCachedSize = size;
+        if (ptNodeArray.mCachedSize != size) {
+            ptNodeArray.mCachedSize = size;
             changed = true;
         }
         return changed;
@@ -363,19 +365,19 @@
      * @param formatOptions file format options.
      * @return the byte size of the entire stack.
      */
-    private static int initializeNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes,
+    private static int initializePtNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes,
             final FormatOptions formatOptions) {
         int nodeArrayOffset = 0;
         for (final PtNodeArray nodeArray : flatNodes) {
             nodeArray.mCachedAddressBeforeUpdate = nodeArrayOffset;
-            int groupCountSize = getGroupCountSize(nodeArray);
-            int groupOffset = 0;
-            for (final CharGroup g : nodeArray.mData) {
-                g.mCachedAddressBeforeUpdate = g.mCachedAddressAfterUpdate =
-                        groupCountSize + nodeArrayOffset + groupOffset;
-                groupOffset += g.mCachedSize;
+            int nodeCountSize = getPtNodeCountSize(nodeArray);
+            int nodeffset = 0;
+            for (final PtNode ptNode : nodeArray.mData) {
+                ptNode.mCachedAddressBeforeUpdate = ptNode.mCachedAddressAfterUpdate =
+                        nodeCountSize + nodeArrayOffset + nodeffset;
+                nodeffset += ptNode.mCachedSize;
             }
-            final int nodeSize = groupCountSize + groupOffset
+            final int nodeSize = nodeCountSize + nodeffset
                     + (formatOptions.mSupportsDynamicUpdate
                             ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
             nodeArrayOffset += nodeArray.mCachedSize;
@@ -388,11 +390,11 @@
      *
      * @param flatNodes the list of node arrays.
      */
-    private static void updateNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes) {
+    private static void updatePtNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes) {
         for (final PtNodeArray nodeArray : flatNodes) {
             nodeArray.mCachedAddressBeforeUpdate = nodeArray.mCachedAddressAfterUpdate;
-            for (final CharGroup g : nodeArray.mData) {
-                g.mCachedAddressBeforeUpdate = g.mCachedAddressAfterUpdate;
+            for (final PtNode ptNode : nodeArray.mData) {
+                ptNode.mCachedAddressBeforeUpdate = ptNode.mCachedAddressAfterUpdate;
             }
         }
     }
@@ -407,38 +409,38 @@
      */
     private static void computeParentAddresses(final ArrayList<PtNodeArray> flatNodes) {
         for (final PtNodeArray nodeArray : flatNodes) {
-            for (final CharGroup group : nodeArray.mData) {
-                if (null != group.mChildren) {
+            for (final PtNode ptNode : nodeArray.mData) {
+                if (null != ptNode.mChildren) {
                     // Assign my address to children's parent address
                     // Here BeforeUpdate and AfterUpdate addresses have the same value, so it
                     // does not matter which we use.
-                    group.mChildren.mCachedParentAddress = group.mCachedAddressAfterUpdate
-                            - group.mChildren.mCachedAddressAfterUpdate;
+                    ptNode.mChildren.mCachedParentAddress = ptNode.mCachedAddressAfterUpdate
+                            - ptNode.mChildren.mCachedAddressAfterUpdate;
                 }
             }
         }
     }
 
     /**
-     * Compute the addresses and sizes of an ordered list of node arrays.
+     * Compute the addresses and sizes of an ordered list of PtNode arrays.
      *
-     * This method takes a list of node arrays and will update their cached address and size
+     * This method takes a list of PtNode arrays and will update their cached address and size
      * values so that they can be written into a file. It determines the smallest size each of the
-     * nodes arrays can be given the addresses of its children and attributes, and store that into
-     * each node.
-     * The order of the node is given by the order of the array. This method makes no effort
+     * PtNode arrays can be given the addresses of its children and attributes, and store that into
+     * each PtNode.
+     * The order of the PtNode is given by the order of the array. This method makes no effort
      * to find a good order; it only mechanically computes the size this order results in.
      *
      * @param dict the dictionary
-     * @param flatNodes the ordered list of nodes arrays
+     * @param flatNodes the ordered list of PtNode arrays
      * @param formatOptions file format options.
      * @return the same array it was passed. The nodes have been updated for address and size.
      */
     private static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict,
             final ArrayList<PtNodeArray> flatNodes, final FormatOptions formatOptions) {
         // First get the worst possible sizes and offsets
-        for (final PtNodeArray n : flatNodes) calculateNodeArrayMaximumSize(n, formatOptions);
-        final int offset = initializeNodeArraysCachedAddresses(flatNodes, formatOptions);
+        for (final PtNodeArray n : flatNodes) calculatePtNodeArrayMaximumSize(n, formatOptions);
+        final int offset = initializePtNodeArraysCachedAddresses(flatNodes, formatOptions);
 
         MakedictLog.i("Compressing the array addresses. Original size : " + offset);
         MakedictLog.i("(Recursively seen size : " + offset + ")");
@@ -447,19 +449,20 @@
         boolean changesDone = false;
         do {
             changesDone = false;
-            int nodeArrayStartOffset = 0;
-            for (final PtNodeArray nodeArray : flatNodes) {
-                nodeArray.mCachedAddressAfterUpdate = nodeArrayStartOffset;
-                final int oldNodeArraySize = nodeArray.mCachedSize;
-                final boolean changed = computeActualNodeArraySize(nodeArray, dict, formatOptions);
-                final int newNodeArraySize = nodeArray.mCachedSize;
+            int ptNodeArrayStartOffset = 0;
+            for (final PtNodeArray ptNodeArray : flatNodes) {
+                ptNodeArray.mCachedAddressAfterUpdate = ptNodeArrayStartOffset;
+                final int oldNodeArraySize = ptNodeArray.mCachedSize;
+                final boolean changed =
+                        computeActualPtNodeArraySize(ptNodeArray, dict, formatOptions);
+                final int newNodeArraySize = ptNodeArray.mCachedSize;
                 if (oldNodeArraySize < newNodeArraySize) {
                     throw new RuntimeException("Increased size ?!");
                 }
-                nodeArrayStartOffset += newNodeArraySize;
+                ptNodeArrayStartOffset += newNodeArraySize;
                 changesDone |= changed;
             }
-            updateNodeArraysCachedAddresses(flatNodes);
+            updatePtNodeArraysCachedAddresses(flatNodes);
             ++passes;
             if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
         } while (changesDone);
@@ -467,10 +470,10 @@
         if (formatOptions.mSupportsDynamicUpdate) {
             computeParentAddresses(flatNodes);
         }
-        final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
+        final PtNodeArray lastPtNodeArray = flatNodes.get(flatNodes.size() - 1);
         MakedictLog.i("Compression complete in " + passes + " passes.");
         MakedictLog.i("After address compression : "
-                + (lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize));
+                + (lastPtNodeArray.mCachedAddressAfterUpdate + lastPtNodeArray.mCachedSize));
 
         return flatNodes;
     }
@@ -478,25 +481,26 @@
     /**
      * Sanity-checking method.
      *
-     * This method checks a list of node arrays for juxtaposition, that is, it will do
+     * This method checks a list of PtNode arrays for juxtaposition, that is, it will do
      * nothing if each node array's cached address is actually the previous node array's address
      * plus the previous node's size.
      * If this is not the case, it will throw an exception.
      *
      * @param arrays the list of node arrays to check
      */
-    private static void checkFlatNodeArrayList(final ArrayList<PtNodeArray> arrays) {
+    private static void checkFlatPtNodeArrayList(final ArrayList<PtNodeArray> arrays) {
         int offset = 0;
         int index = 0;
-        for (final PtNodeArray nodeArray : arrays) {
+        for (final PtNodeArray ptNodeArray : arrays) {
             // BeforeUpdate and AfterUpdate addresses are the same here, so it does not matter
             // which we use.
-            if (nodeArray.mCachedAddressAfterUpdate != offset) {
+            if (ptNodeArray.mCachedAddressAfterUpdate != offset) {
                 throw new RuntimeException("Wrong address for node " + index
-                        + " : expected " + offset + ", got " + nodeArray.mCachedAddressAfterUpdate);
+                        + " : expected " + offset + ", got " +
+                        ptNodeArray.mCachedAddressAfterUpdate);
             }
             ++index;
-            offset += nodeArray.mCachedSize;
+            offset += ptNodeArray.mCachedSize;
         }
     }
 
@@ -552,19 +556,19 @@
     }
 
     /**
-     * Makes the flag value for a char group.
+     * Makes the flag value for a PtNode.
      *
-     * @param hasMultipleChars whether the group has multiple chars.
-     * @param isTerminal whether the group is terminal.
+     * @param hasMultipleChars whether the PtNode has multiple chars.
+     * @param isTerminal whether the PtNode is terminal.
      * @param childrenAddressSize the size of a children address.
-     * @param hasShortcuts whether the group has shortcuts.
-     * @param hasBigrams whether the group has bigrams.
-     * @param isNotAWord whether the group is not a word.
-     * @param isBlackListEntry whether the group is a blacklist entry.
+     * @param hasShortcuts whether the PtNode has shortcuts.
+     * @param hasBigrams whether the PtNode has bigrams.
+     * @param isNotAWord whether the PtNode is not a word.
+     * @param isBlackListEntry whether the PtNode is a blacklist entry.
      * @param formatOptions file format options.
      * @return the flags
      */
-    static int makeCharGroupFlags(final boolean hasMultipleChars, final boolean isTerminal,
+    static int makePtNodeFlags(final boolean hasMultipleChars, final boolean isTerminal,
             final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
             final boolean isNotAWord, final boolean isBlackListEntry,
             final FormatOptions formatOptions) {
@@ -576,16 +580,16 @@
         } else if (true) {
             switch (childrenAddressSize) {
                 case 1:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE;
                     break;
                 case 2:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES;
                     break;
                 case 3:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES;
                     break;
                 case 0:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS;
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS;
                     break;
                 default:
                     throw new RuntimeException("Node with a strange address");
@@ -598,12 +602,12 @@
         return flags;
     }
 
-    private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
+    private static byte makePtNodeFlags(final PtNode node, final int ptNodeAddress,
             final int childrenOffset, final FormatOptions formatOptions) {
-        return (byte) makeCharGroupFlags(group.mChars.length > 1, group.mFrequency >= 0,
+        return (byte) makePtNodeFlags(node.mChars.length > 1, node.mFrequency >= 0,
                 getByteSize(childrenOffset),
-                group.mShortcutTargets != null && !group.mShortcutTargets.isEmpty(),
-                group.mBigrams != null, group.mIsNotAWord, group.mIsBlacklistEntry, formatOptions);
+                node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(),
+                node.mBigrams != null, node.mIsNotAWord, node.mIsBlacklistEntry, formatOptions);
     }
 
     /**
@@ -618,17 +622,17 @@
      */
     private static final int makeBigramFlags(final boolean more, final int offset,
             int bigramFrequency, final int unigramFrequency, final String word) {
-        int bigramFlags = (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
-                + (offset < 0 ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0);
+        int bigramFlags = (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0)
+                + (offset < 0 ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0);
         switch (getByteSize(offset)) {
         case 1:
-            bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+            bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE;
             break;
         case 2:
-            bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+            bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES;
             break;
         case 3:
-            bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+            bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES;
             break;
         default:
             throw new RuntimeException("Strange offset size");
@@ -673,7 +677,7 @@
         // small over-estimation that we get in this case. TODO: actually remove this bigram
         // if discretizedFrequency < 0.
         final int finalBigramFrequency = discretizedFrequency > 0 ? discretizedFrequency : 0;
-        bigramFlags += finalBigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
+        bigramFlags += finalBigramFrequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY;
         return bigramFlags;
     }
 
@@ -698,8 +702,8 @@
      * @return the flags
      */
     static final int makeShortcutFlags(final boolean more, final int frequency) {
-        return (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
-                + (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY);
+        return (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0)
+                + (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY);
     }
 
     private static final int writeParentAddress(final byte[] buffer, final int index,
@@ -722,68 +726,69 @@
     }
 
     /**
-     * Write a node array to memory. The node array is expected to have its final position cached.
+     * Write a PtNodeArray to memory. The PtNodeArray is expected to have its final position cached.
      *
      * @param dict the dictionary the node array is a part of (for relative offsets).
      * @param buffer the memory buffer to write to.
-     * @param nodeArray the node array to write.
+     * @param ptNodeArray the node array to write.
      * @param formatOptions file format options.
      * @return the address of the END of the node.
      */
     @SuppressWarnings("unused")
     private static int writePlacedNode(final FusionDictionary dict, byte[] buffer,
-            final PtNodeArray nodeArray, final FormatOptions formatOptions) {
-        // TODO: Make the code in common with BinaryDictIOUtils#writeCharGroup
-        int index = nodeArray.mCachedAddressAfterUpdate;
+            final PtNodeArray ptNodeArray, final FormatOptions formatOptions) {
+        // TODO: Make the code in common with BinaryDictIOUtils#writePtNode
+        int index = ptNodeArray.mCachedAddressAfterUpdate;
 
-        final int groupCount = nodeArray.mData.size();
-        final int countSize = getGroupCountSize(nodeArray);
-        final int parentAddress = nodeArray.mCachedParentAddress;
+        final int ptNodeCount = ptNodeArray.mData.size();
+        final int countSize = getPtNodeCountSize(ptNodeArray);
+        final int parentAddress = ptNodeArray.mCachedParentAddress;
         if (1 == countSize) {
-            buffer[index++] = (byte)groupCount;
+            buffer[index++] = (byte)ptNodeCount;
         } else if (2 == countSize) {
             // We need to signal 2-byte size by setting the top bit of the MSB to 1, so
             // we | 0x80 to do this.
-            buffer[index++] = (byte)((groupCount >> 8) | 0x80);
-            buffer[index++] = (byte)(groupCount & 0xFF);
+            buffer[index++] = (byte)((ptNodeCount >> 8) | 0x80);
+            buffer[index++] = (byte)(ptNodeCount & 0xFF);
         } else {
             throw new RuntimeException("Strange size from getGroupCountSize : " + countSize);
         }
-        int groupAddress = index;
-        for (int i = 0; i < groupCount; ++i) {
-            final CharGroup group = nodeArray.mData.get(i);
-            if (index != group.mCachedAddressAfterUpdate) {
+        int ptNodeAddress = index;
+        for (int i = 0; i < ptNodeCount; ++i) {
+            final PtNode ptNode = ptNodeArray.mData.get(i);
+            if (index != ptNode.mCachedAddressAfterUpdate) {
                 throw new RuntimeException("Bug: write index is not the same as the cached address "
-                        + "of the group : " + index + " <> " + group.mCachedAddressAfterUpdate);
+                        + "of the node : " + index + " <> " + ptNode.mCachedAddressAfterUpdate);
             }
-            groupAddress += getGroupHeaderSize(group, formatOptions);
+            ptNodeAddress += getNodeHeaderSize(ptNode, formatOptions);
             // Sanity checks.
-            if (DBG && group.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) {
+            if (DBG && ptNode.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) {
                 throw new RuntimeException("A node has a frequency > "
                         + FormatSpec.MAX_TERMINAL_FREQUENCY
-                        + " : " + group.mFrequency);
+                        + " : " + ptNode.mFrequency);
             }
-            if (group.mFrequency >= 0) groupAddress += FormatSpec.GROUP_FREQUENCY_SIZE;
-            final int childrenOffset = null == group.mChildren
+            if (ptNode.mFrequency >= 0) ptNodeAddress += FormatSpec.PTNODE_FREQUENCY_SIZE;
+            final int childrenOffset = null == ptNode.mChildren
                     ? FormatSpec.NO_CHILDREN_ADDRESS
-                            : group.mChildren.mCachedAddressAfterUpdate - groupAddress;
+                            : ptNode.mChildren.mCachedAddressAfterUpdate - ptNodeAddress;
             buffer[index++] =
-                    makeCharGroupFlags(group, groupAddress, childrenOffset, formatOptions);
+                    makePtNodeFlags(ptNode, ptNodeAddress, childrenOffset, formatOptions);
 
             if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) {
                 index = writeParentAddress(buffer, index, parentAddress, formatOptions);
             } else {
                 index = writeParentAddress(buffer, index, parentAddress
-                        + (nodeArray.mCachedAddressAfterUpdate - group.mCachedAddressAfterUpdate),
+                        + (ptNodeArray.mCachedAddressAfterUpdate
+                                - ptNode.mCachedAddressAfterUpdate),
                         formatOptions);
             }
 
-            index = CharEncoding.writeCharArray(group.mChars, buffer, index);
-            if (group.hasSeveralChars()) {
-                buffer[index++] = FormatSpec.GROUP_CHARACTERS_TERMINATOR;
+            index = CharEncoding.writeCharArray(ptNode.mChars, buffer, index);
+            if (ptNode.hasSeveralChars()) {
+                buffer[index++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
             }
-            if (group.mFrequency >= 0) {
-                buffer[index++] = (byte) group.mFrequency;
+            if (ptNode.mFrequency >= 0) {
+                buffer[index++] = (byte) ptNode.mFrequency;
             }
 
             final int shift;
@@ -793,23 +798,24 @@
                 shift = writeVariableAddress(buffer, index, childrenOffset);
             }
             index += shift;
-            groupAddress += shift;
+            ptNodeAddress += shift;
 
             // Write shortcuts
-            if (null != group.mShortcutTargets && !group.mShortcutTargets.isEmpty()) {
+            if (null != ptNode.mShortcutTargets && !ptNode.mShortcutTargets.isEmpty()) {
                 final int indexOfShortcutByteSize = index;
-                index += FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
-                groupAddress += FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
-                final Iterator<WeightedString> shortcutIterator = group.mShortcutTargets.iterator();
+                index += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
+                ptNodeAddress += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
+                final Iterator<WeightedString> shortcutIterator =
+                        ptNode.mShortcutTargets.iterator();
                 while (shortcutIterator.hasNext()) {
                     final WeightedString target = shortcutIterator.next();
-                    ++groupAddress;
+                    ++ptNodeAddress;
                     int shortcutFlags = makeShortcutFlags(shortcutIterator.hasNext(),
                             target.mFrequency);
                     buffer[index++] = (byte)shortcutFlags;
                     final int shortcutShift = CharEncoding.writeString(buffer, index, target.mWord);
                     index += shortcutShift;
-                    groupAddress += shortcutShift;
+                    ptNodeAddress += shortcutShift;
                 }
                 final int shortcutByteSize = index - indexOfShortcutByteSize;
                 if (shortcutByteSize > 0xFFFF) {
@@ -819,22 +825,22 @@
                 buffer[indexOfShortcutByteSize + 1] = (byte)(shortcutByteSize & 0xFF);
             }
             // Write bigrams
-            if (null != group.mBigrams) {
-                final Iterator<WeightedString> bigramIterator = group.mBigrams.iterator();
+            if (null != ptNode.mBigrams) {
+                final Iterator<WeightedString> bigramIterator = ptNode.mBigrams.iterator();
                 while (bigramIterator.hasNext()) {
                     final WeightedString bigram = bigramIterator.next();
-                    final CharGroup target =
+                    final PtNode target =
                             FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
                     final int addressOfBigram = target.mCachedAddressAfterUpdate;
                     final int unigramFrequencyForThisWord = target.mFrequency;
-                    ++groupAddress;
-                    final int offset = addressOfBigram - groupAddress;
+                    ++ptNodeAddress;
+                    final int offset = addressOfBigram - ptNodeAddress;
                     int bigramFlags = makeBigramFlags(bigramIterator.hasNext(), offset,
                             bigram.mFrequency, unigramFrequencyForThisWord, bigram.mWord);
                     buffer[index++] = (byte)bigramFlags;
                     final int bigramShift = writeVariableAddress(buffer, index, Math.abs(offset));
                     index += bigramShift;
-                    groupAddress += bigramShift;
+                    ptNodeAddress += bigramShift;
                 }
             }
 
@@ -844,64 +850,64 @@
                     = FormatSpec.NO_FORWARD_LINK_ADDRESS;
             index += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
         }
-        if (index != nodeArray.mCachedAddressAfterUpdate + nodeArray.mCachedSize) {
+        if (index != ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize) {
             throw new RuntimeException(
-                    "Not the same size : written " + (index - nodeArray.mCachedAddressAfterUpdate)
-                     + " bytes from a node that should have " + nodeArray.mCachedSize + " bytes");
+                    "Not the same size : written " + (index - ptNodeArray.mCachedAddressAfterUpdate)
+                     + " bytes from a node that should have " + ptNodeArray.mCachedSize + " bytes");
         }
         return index;
     }
 
     /**
-     * Dumps a collection of useful statistics about a list of node arrays.
+     * Dumps a collection of useful statistics about a list of PtNode arrays.
      *
      * This prints purely informative stuff, like the total estimated file size, the
-     * number of node arrays, of character groups, the repartition of each address size, etc
+     * number of PtNode arrays, of PtNodes, the repartition of each address size, etc
      *
-     * @param nodeArrays the list of node arrays.
+     * @param ptNodeArrays the list of PtNode arrays.
      */
-    private static void showStatistics(ArrayList<PtNodeArray> nodeArrays) {
+    private static void showStatistics(ArrayList<PtNodeArray> ptNodeArrays) {
         int firstTerminalAddress = Integer.MAX_VALUE;
         int lastTerminalAddress = Integer.MIN_VALUE;
         int size = 0;
-        int charGroups = 0;
-        int maxGroups = 0;
+        int ptNodes = 0;
+        int maxNodes = 0;
         int maxRuns = 0;
-        for (final PtNodeArray nodeArray : nodeArrays) {
-            if (maxGroups < nodeArray.mData.size()) maxGroups = nodeArray.mData.size();
-            for (final CharGroup cg : nodeArray.mData) {
-                ++charGroups;
-                if (cg.mChars.length > maxRuns) maxRuns = cg.mChars.length;
-                if (cg.mFrequency >= 0) {
-                    if (nodeArray.mCachedAddressAfterUpdate < firstTerminalAddress)
-                        firstTerminalAddress = nodeArray.mCachedAddressAfterUpdate;
-                    if (nodeArray.mCachedAddressAfterUpdate > lastTerminalAddress)
-                        lastTerminalAddress = nodeArray.mCachedAddressAfterUpdate;
+        for (final PtNodeArray ptNodeArray : ptNodeArrays) {
+            if (maxNodes < ptNodeArray.mData.size()) maxNodes = ptNodeArray.mData.size();
+            for (final PtNode ptNode : ptNodeArray.mData) {
+                ++ptNodes;
+                if (ptNode.mChars.length > maxRuns) maxRuns = ptNode.mChars.length;
+                if (ptNode.mFrequency >= 0) {
+                    if (ptNodeArray.mCachedAddressAfterUpdate < firstTerminalAddress)
+                        firstTerminalAddress = ptNodeArray.mCachedAddressAfterUpdate;
+                    if (ptNodeArray.mCachedAddressAfterUpdate > lastTerminalAddress)
+                        lastTerminalAddress = ptNodeArray.mCachedAddressAfterUpdate;
                 }
             }
-            if (nodeArray.mCachedAddressAfterUpdate + nodeArray.mCachedSize > size) {
-                size = nodeArray.mCachedAddressAfterUpdate + nodeArray.mCachedSize;
+            if (ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize > size) {
+                size = ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize;
             }
         }
-        final int[] groupCounts = new int[maxGroups + 1];
+        final int[] ptNodeCounts = new int[maxNodes + 1];
         final int[] runCounts = new int[maxRuns + 1];
-        for (final PtNodeArray nodeArray : nodeArrays) {
-            ++groupCounts[nodeArray.mData.size()];
-            for (final CharGroup cg : nodeArray.mData) {
-                ++runCounts[cg.mChars.length];
+        for (final PtNodeArray ptNodeArray : ptNodeArrays) {
+            ++ptNodeCounts[ptNodeArray.mData.size()];
+            for (final PtNode ptNode : ptNodeArray.mData) {
+                ++runCounts[ptNode.mChars.length];
             }
         }
 
         MakedictLog.i("Statistics:\n"
                 + "  total file size " + size + "\n"
-                + "  " + nodeArrays.size() + " node arrays\n"
-                + "  " + charGroups + " groups (" + ((float)charGroups / nodeArrays.size())
-                        + " groups per node)\n"
+                + "  " + ptNodeArrays.size() + " node arrays\n"
+                + "  " + ptNodes + " PtNodes (" + ((float)ptNodes / ptNodeArrays.size())
+                        + " PtNodes per node)\n"
                 + "  first terminal at " + firstTerminalAddress + "\n"
                 + "  last terminal at " + lastTerminalAddress + "\n"
-                + "  Group stats : max = " + maxGroups);
-        for (int i = 0; i < groupCounts.length; ++i) {
-            MakedictLog.i("    " + i + " : " + groupCounts[i]);
+                + "  PtNode stats : max = " + maxNodes);
+        for (int i = 0; i < ptNodeCounts.length; ++i) {
+            MakedictLog.i("    " + i + " : " + ptNodeCounts[i]);
         }
         MakedictLog.i("  Character run stats : max = " + maxRuns);
         for (int i = 0; i < runCounts.length; ++i) {
@@ -922,7 +928,7 @@
 
         // Addresses are limited to 3 bytes, but since addresses can be relative to each node
         // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding
-        // the order of the node arrays becomes a quite complicated problem, because though the
+        // the order of the PtNode arrays becomes a quite complicated problem, because though the
         // dictionary itself does not have a size limit, each node array must still be within 16MB
         // of all its children and parents. As long as this is ensured, the dictionary file may
         // grow to any size.
@@ -980,8 +986,8 @@
 
         MakedictLog.i("Computing addresses...");
         computeAddresses(dict, flatNodes, formatOptions);
-        MakedictLog.i("Checking array...");
-        if (DBG) checkFlatNodeArrayList(flatNodes);
+        MakedictLog.i("Checking PtNode array...");
+        if (DBG) checkFlatPtNodeArrayList(flatNodes);
 
         // Create a buffer that matches the final dictionary size.
         final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 323afb4..a08e28c 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -22,7 +22,7 @@
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
 
@@ -44,17 +44,17 @@
     }
 
     private static final class Position {
-        public static final int NOT_READ_GROUPCOUNT = -1;
+        public static final int NOT_READ_PTNODE_COUNT = -1;
 
         public int mAddress;
-        public int mNumOfCharGroup;
+        public int mNumOfPtNode;
         public int mPosition;
         public int mLength;
 
         public Position(int address, int length) {
             mAddress = address;
             mLength = length;
-            mNumOfCharGroup = NOT_READ_GROUPCOUNT;
+            mNumOfPtNode = NOT_READ_PTNODE_COUNT;
         }
     }
 
@@ -79,45 +79,45 @@
             Position p = stack.peek();
 
             if (DBG) {
-                MakedictLog.d("read: address=" + p.mAddress + ", numOfCharGroup=" +
-                        p.mNumOfCharGroup + ", position=" + p.mPosition + ", length=" + p.mLength);
+                MakedictLog.d("read: address=" + p.mAddress + ", numOfPtNode=" +
+                        p.mNumOfPtNode + ", position=" + p.mPosition + ", length=" + p.mLength);
             }
 
             if (dictBuffer.position() != p.mAddress) dictBuffer.position(p.mAddress);
             if (index != p.mLength) index = p.mLength;
 
-            if (p.mNumOfCharGroup == Position.NOT_READ_GROUPCOUNT) {
-                p.mNumOfCharGroup = BinaryDictDecoderUtils.readCharGroupCount(dictBuffer);
-                p.mAddress += getGroupCountSize(p.mNumOfCharGroup);
+            if (p.mNumOfPtNode == Position.NOT_READ_PTNODE_COUNT) {
+                p.mNumOfPtNode = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+                p.mAddress += getPtNodeCountSize(p.mNumOfPtNode);
                 p.mPosition = 0;
             }
-            if (p.mNumOfCharGroup == 0) {
+            if (p.mNumOfPtNode == 0) {
                 stack.pop();
                 continue;
             }
-            CharGroupInfo info = dictDecoder.readPtNode(p.mAddress, formatOptions);
+            PtNodeInfo info = dictDecoder.readPtNode(p.mAddress, formatOptions);
             for (int i = 0; i < info.mCharacters.length; ++i) {
                 pushedChars[index++] = info.mCharacters[i];
             }
             p.mPosition++;
 
-            final boolean isMovedGroup = isMovedGroup(info.mFlags,
+            final boolean isMovedPtNode = isMovedPtNode(info.mFlags,
                     formatOptions);
-            final boolean isDeletedGroup = isDeletedGroup(info.mFlags,
+            final boolean isDeletedPtNode = isDeletedPtNode(info.mFlags,
                     formatOptions);
-            if (!isMovedGroup && !isDeletedGroup
-                    && info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word
+            if (!isMovedPtNode && !isDeletedPtNode
+                    && info.mFrequency != FusionDictionary.PtNode.NOT_A_TERMINAL) {// found word
                 words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
                 frequencies.put(info.mOriginalAddress, info.mFrequency);
                 if (info.mBigrams != null) bigrams.put(info.mOriginalAddress, info.mBigrams);
             }
 
-            if (p.mPosition == p.mNumOfCharGroup) {
+            if (p.mPosition == p.mNumOfPtNode) {
                 if (formatOptions.mSupportsDynamicUpdate) {
                     final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
                     if (forwardLinkAddress != FormatSpec.NO_FORWARD_LINK_ADDRESS) {
                         // The node array has a forward link.
-                        p.mNumOfCharGroup = Position.NOT_READ_GROUPCOUNT;
+                        p.mNumOfPtNode = Position.NOT_READ_PTNODE_COUNT;
                         p.mAddress = forwardLinkAddress;
                     } else {
                         stack.pop();
@@ -126,11 +126,11 @@
                     stack.pop();
                 }
             } else {
-                // The node array has more groups.
+                // The Ptnode array has more PtNodes.
                 p.mAddress = dictBuffer.position();
             }
 
-            if (!isMovedGroup && hasChildrenAddress(info.mChildrenAddress)) {
+            if (!isMovedPtNode && hasChildrenAddress(info.mChildrenAddress)) {
                 final Position childrenPos = new Position(info.mChildrenAddress, index);
                 stack.push(childrenPos);
             }
@@ -159,7 +159,7 @@
     }
 
     /**
-     * Gets the address of the last CharGroup of the exact matching word in the dictionary.
+     * Gets the address of the last PtNode of the exact matching word in the dictionary.
      * If no match is found, returns NOT_VALID_WORD.
      *
      * @param dictDecoder the dict decoder.
@@ -169,7 +169,7 @@
      * @throws UnsupportedFormatException if the format of the file is not recognized.
      */
     @UsedForTesting
-    public static int getTerminalPosition(final Ver3DictDecoder dictDecoder,
+    /* package */ static int getTerminalPosition(final Ver3DictDecoder dictDecoder,
             final String word) throws IOException, UnsupportedFormatException {
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
         if (word == null) return FormatSpec.NOT_VALID_WORD;
@@ -182,17 +182,17 @@
             if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD;
 
             do {
-                final int charGroupCount = BinaryDictDecoderUtils.readCharGroupCount(dictBuffer);
-                boolean foundNextCharGroup = false;
-                for (int i = 0; i < charGroupCount; ++i) {
-                    final int charGroupPos = dictBuffer.position();
-                    final CharGroupInfo currentInfo = dictDecoder.readPtNode(charGroupPos,
+                final int ptNodeCount = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+                boolean foundNextPtNode = false;
+                for (int i = 0; i < ptNodeCount; ++i) {
+                    final int ptNodePos = dictBuffer.position();
+                    final PtNodeInfo currentInfo = dictDecoder.readPtNode(ptNodePos,
                             header.mFormatOptions);
-                    final boolean isMovedGroup = isMovedGroup(currentInfo.mFlags,
+                    final boolean isMovedNode = isMovedPtNode(currentInfo.mFlags,
                             header.mFormatOptions);
-                    final boolean isDeletedGroup = isDeletedGroup(currentInfo.mFlags,
+                    final boolean isDeletedNode = isDeletedPtNode(currentInfo.mFlags,
                             header.mFormatOptions);
-                    if (isMovedGroup) continue;
+                    if (isMovedNode) continue;
                     boolean same = true;
                     for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
                             p < currentInfo.mCharacters.length;
@@ -205,30 +205,30 @@
                     }
 
                     if (same) {
-                        // found the group matches the word.
+                        // found the PtNode matches the word.
                         if (wordPos + currentInfo.mCharacters.length == wordLen) {
-                            if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL
-                                    || isDeletedGroup) {
+                            if (currentInfo.mFrequency == PtNode.NOT_A_TERMINAL
+                                    || isDeletedNode) {
                                 return FormatSpec.NOT_VALID_WORD;
                             } else {
-                                return charGroupPos;
+                                return ptNodePos;
                             }
                         }
                         wordPos += currentInfo.mCharacters.length;
                         if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
                             return FormatSpec.NOT_VALID_WORD;
                         }
-                        foundNextCharGroup = true;
+                        foundNextPtNode = true;
                         dictBuffer.position(currentInfo.mChildrenAddress);
                         break;
                     }
                 }
 
-                // If we found the next char group, it is under the file pointer.
+                // If we found the next PtNode, it is under the file pointer.
                 // But if not, we are at the end of this node array so we expect to have
                 // a forward link address that we need to consult and possibly resume
                 // search on the next node array in the linked list.
-                if (foundNextCharGroup) break;
+                if (foundNextPtNode) break;
                 if (!header.mFormatOptions.mSupportsDynamicUpdate) {
                     return FormatSpec.NOT_VALID_WORD;
                 }
@@ -289,8 +289,7 @@
         return BinaryDictEncoderUtils.getByteSize(value);
     }
 
-    static void skipCharGroup(final DictBuffer dictBuffer,
-            final FormatOptions formatOptions) {
+    static void skipPtNode(final DictBuffer dictBuffer, final FormatOptions formatOptions) {
         final int flags = dictBuffer.readUnsignedByte();
         BinaryDictDecoderUtils.readParentAddress(dictBuffer, formatOptions);
         skipString(dictBuffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
@@ -299,27 +298,27 @@
         if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
             final int shortcutsSize = dictBuffer.readUnsignedShort();
             dictBuffer.position(dictBuffer.position() + shortcutsSize
-                    - FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE);
+                    - FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
         }
         if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) {
             int bigramCount = 0;
-            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
                 final int bigramFlags = dictBuffer.readUnsignedByte();
-                switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
+                switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
                         dictBuffer.readUnsignedByte();
                         break;
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
                         dictBuffer.readUnsignedShort();
                         break;
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
                         dictBuffer.readUnsignedInt24();
                         break;
                 }
-                if ((bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT) == 0) break;
+                if ((bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) == 0) break;
             }
-            if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
-                throw new RuntimeException("Too many bigrams in a group.");
+            if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+                throw new RuntimeException("Too many bigrams in a PtNode.");
             }
         }
     }
@@ -360,24 +359,24 @@
                 size += 3;
             }
         }
-        destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
-        size += FormatSpec.GROUP_TERMINATOR_SIZE;
+        destination.write((byte)FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
+        size += FormatSpec.PTNODE_TERMINATOR_SIZE;
         return size;
     }
 
     /**
-     * Write a char group to an output stream from a CharGroupInfo.
-     * A char group is an in-memory representation of a node in the patricia trie.
-     * A char group info is a container for low-level information about how the
-     * char group is stored in the binary format.
+     * Write a PtNode to an output stream from a PtNodeInfo.
+     * A PtNode is an in-memory representation of a node in the patricia trie.
+     * A PtNode info is a container for low-level information about how the
+     * PtNode is stored in the binary format.
      *
      * @param destination the stream to write.
-     * @param info the char group info to be written.
+     * @param info the PtNode info to be written.
      * @return the size written, in bytes.
      */
-    private static int writeCharGroup(final OutputStream destination, final CharGroupInfo info)
+    private static int writePtNode(final OutputStream destination, final PtNodeInfo info)
             throws IOException {
-        int size = FormatSpec.GROUP_FLAGS_SIZE;
+        int size = FormatSpec.PTNODE_FLAGS_SIZE;
         destination.write((byte)info.mFlags);
         final int parentOffset = info.mParentAddress == FormatSpec.NO_PARENT_ADDRESS ?
                 FormatSpec.NO_PARENT_ADDRESS : info.mParentAddress - info.mOriginalAddress;
@@ -392,7 +391,7 @@
             }
         }
         if (info.mCharacters.length > 1) {
-            destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+            destination.write((byte)FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
             size++;
         }
 
@@ -402,7 +401,7 @@
         }
 
         if (DBG) {
-            MakedictLog.d("writeCharGroup origin=" + info.mOriginalAddress + ", size=" + size
+            MakedictLog.d("writePtNode origin=" + info.mOriginalAddress + ", size=" + size
                     + ", child=" + info.mChildrenAddress + ", characters ="
                     + new String(info.mCharacters, 0, info.mCharacters.length));
         }
@@ -434,23 +433,23 @@
 
                 final int bigramFrequency = info.mBigrams.get(i).mFrequency;
                 int bigramFlags = (i < info.mBigrams.size() - 1)
-                        ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0;
+                        ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0;
                 size++;
                 final int bigramOffset = info.mBigrams.get(i).mAddress - (info.mOriginalAddress
                         + size);
-                bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0;
+                bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0;
                 switch (BinaryDictEncoderUtils.getByteSize(bigramOffset)) {
                 case 1:
-                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+                    bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE;
                     break;
                 case 2:
-                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+                    bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES;
                     break;
                 case 3:
-                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+                    bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES;
                     break;
                 }
-                bigramFlags |= bigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
+                bigramFlags |= bigramFrequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY;
                 destination.write((byte)bigramFlags);
                 size += writeVariableAddress(destination, Math.abs(bigramOffset));
             }
@@ -459,21 +458,21 @@
     }
 
     /**
-     * Compute the size of the char group.
+     * Compute the size of the PtNode.
      */
-    static int computeGroupSize(final CharGroupInfo info, final FormatOptions formatOptions) {
-        int size = FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
-                + BinaryDictEncoderUtils.getGroupCharactersSize(info.mCharacters)
+    static int computePtNodeSize(final PtNodeInfo info, final FormatOptions formatOptions) {
+        int size = FormatSpec.PTNODE_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
+                + BinaryDictEncoderUtils.getPtNodeCharactersSize(info.mCharacters)
                 + getChildrenAddressSize(info.mFlags, formatOptions);
         if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
-            size += FormatSpec.GROUP_FREQUENCY_SIZE;
+            size += FormatSpec.PTNODE_FREQUENCY_SIZE;
         }
         if (info.mShortcutTargets != null && !info.mShortcutTargets.isEmpty()) {
             size += BinaryDictEncoderUtils.getShortcutListSize(info.mShortcutTargets);
         }
         if (info.mBigrams != null) {
             for (final PendingAttribute attr : info.mBigrams) {
-                size += FormatSpec.GROUP_FLAGS_SIZE;
+                size += FormatSpec.PTNODE_FLAGS_SIZE;
                 size += BinaryDictEncoderUtils.getByteSize(attr.mAddress);
             }
         }
@@ -484,14 +483,14 @@
      * Write a node array to the stream.
      *
      * @param destination the stream to write.
-     * @param infos an array of CharGroupInfo to be written.
+     * @param infos an array of PtNodeInfo to be written.
      * @return the size written, in bytes.
      * @throws IOException
      */
-    static int writeNodes(final OutputStream destination, final CharGroupInfo[] infos)
+    static int writeNodes(final OutputStream destination, final PtNodeInfo[] infos)
             throws IOException {
-        int size = getGroupCountSize(infos.length);
-        switch (getGroupCountSize(infos.length)) {
+        int size = getPtNodeCountSize(infos.length);
+        switch (getPtNodeCountSize(infos.length)) {
             case 1:
                 destination.write((byte)infos.length);
                 break;
@@ -500,9 +499,9 @@
                 destination.write((byte)(infos.length & 0xFF));
                 break;
             default:
-                throw new RuntimeException("Invalid group count size.");
+                throw new RuntimeException("Invalid node count size.");
         }
-        for (final CharGroupInfo info : infos) size += writeCharGroup(destination, info);
+        for (final PtNodeInfo info : infos) size += writePtNode(destination, info);
         writeSInt24ToStream(destination, FormatSpec.NO_FORWARD_LINK_ADDRESS);
         return size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
     }
@@ -560,11 +559,11 @@
     }
 
     /**
-     * Helper method to check whether the group is moved.
+     * Helper method to check whether the node is moved.
      */
-    public static boolean isMovedGroup(final int flags, final FormatOptions options) {
+    public static boolean isMovedPtNode(final int flags, final FormatOptions options) {
         return options.mSupportsDynamicUpdate
-                && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
+                && ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
     }
 
     /**
@@ -576,26 +575,26 @@
     }
 
     /**
-     * Helper method to check whether the group is deleted.
+     * Helper method to check whether the node is deleted.
      */
-    public static boolean isDeletedGroup(final int flags, final FormatOptions formatOptions) {
+    public static boolean isDeletedPtNode(final int flags, final FormatOptions formatOptions) {
         return formatOptions.mSupportsDynamicUpdate
-                && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
+                && ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
     }
 
     /**
-     * Compute the binary size of the group count
-     * @param count the group count
-     * @return the size of the group count, either 1 or 2 bytes.
+     * Compute the binary size of the node count
+     * @param count the node count
+     * @return the size of the node count, either 1 or 2 bytes.
      */
-    public static int getGroupCountSize(final int count) {
-        if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= count) {
+    public static int getPtNodeCountSize(final int count) {
+        if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= count) {
             return 1;
-        } else if (FormatSpec.MAX_CHARGROUPS_IN_A_PT_NODE_ARRAY >= count) {
+        } else if (FormatSpec.MAX_PTNODES_IN_A_PT_NODE_ARRAY >= count) {
             return 2;
         } else {
             throw new RuntimeException("Can't have more than "
-                    + FormatSpec.MAX_CHARGROUPS_IN_A_PT_NODE_ARRAY + " groups in a node (found "
+                    + FormatSpec.MAX_PTNODES_IN_A_PT_NODE_ARRAY + " PtNode in a PtNodeArray (found "
                     + count + ")");
         }
     }
@@ -603,14 +602,14 @@
     static int getChildrenAddressSize(final int optionFlags,
             final FormatOptions formatOptions) {
         if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
-        switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
+        switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
                 return 1;
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
                 return 2;
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
                 return 3;
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
             default:
                 return 0;
         }
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
index 13a42fa..d5fcacc 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
@@ -40,9 +40,9 @@
      * Reads PtNode from nodeAddress.
      * @param ptNodePos the position of PtNode.
      * @param formatOptions the format options.
-     * @return CharGroupInfo.
+     * @return PtNodeInfo.
      */
-    public CharGroupInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions);
+    public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions);
 
     /**
      * Reads a buffer and returns the memory representation of the dictionary.
@@ -58,6 +58,19 @@
     public FusionDictionary readDictionaryBinary(final FusionDictionary dict)
             throws FileNotFoundException, IOException, UnsupportedFormatException;
 
+    /**
+     * Gets the address of the last PtNode of the exact matching word in the dictionary.
+     * If no match is found, returns NOT_VALID_WORD.
+     *
+     * @param word the word we search for.
+     * @return the address of the terminal node.
+     * @throws IOException if the file can't be read.
+     * @throws UnsupportedFormatException if the format of the file is not recognized.
+     */
+    @UsedForTesting
+    public int getTerminalPosition(final String word)
+            throws IOException, UnsupportedFormatException;
+
     // Flags for DictionaryBufferFactory.
     public static final int USE_READONLY_BYTEBUFFER = 0x01000000;
     public static final int USE_BYTEARRAY = 0x02000000;
diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
index f976c81..bf3d191 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
@@ -43,7 +43,7 @@
     }
 
     private static int markAsDeleted(final int flags) {
-        return (flags & (~FormatSpec.MASK_GROUP_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
+        return (flags & (~FormatSpec.MASK_CHILDREN_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
     }
 
     /**
@@ -60,7 +60,7 @@
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
         dictBuffer.position(0);
         final FileHeader header = dictDecoder.readHeader();
-        final int wordPosition = BinaryDictIOUtils.getTerminalPosition(dictDecoder, word);
+        final int wordPosition = dictDecoder.getTerminalPosition(word);
         if (wordPosition == FormatSpec.NOT_VALID_WORD) return;
 
         dictBuffer.position(wordPosition);
@@ -70,56 +70,56 @@
     }
 
     /**
-     * Update a parent address in a CharGroup that is referred to by groupOriginAddress.
+     * Update a parent address in a PtNode that is referred to by ptNodeOriginAddress.
      *
      * @param dictBuffer the DictBuffer to write.
-     * @param groupOriginAddress the address of the group.
+     * @param ptNodeOriginAddress the address of the PtNode.
      * @param newParentAddress the absolute address of the parent.
      * @param formatOptions file format options.
      */
     public static void updateParentAddress(final DictBuffer dictBuffer,
-            final int groupOriginAddress, final int newParentAddress,
+            final int ptNodeOriginAddress, final int newParentAddress,
             final FormatOptions formatOptions) {
         final int originalPosition = dictBuffer.position();
-        dictBuffer.position(groupOriginAddress);
+        dictBuffer.position(ptNodeOriginAddress);
         if (!formatOptions.mSupportsDynamicUpdate) {
             throw new RuntimeException("this file format does not support parent addresses");
         }
         final int flags = dictBuffer.readUnsignedByte();
-        if (BinaryDictIOUtils.isMovedGroup(flags, formatOptions)) {
-            // If the group is moved, the parent address is stored in the destination group.
-            // We are guaranteed to process the destination group later, so there is no need to
+        if (BinaryDictIOUtils.isMovedPtNode(flags, formatOptions)) {
+            // If the node is moved, the parent address is stored in the destination node.
+            // We are guaranteed to process the destination node later, so there is no need to
             // update anything here.
             dictBuffer.position(originalPosition);
             return;
         }
         if (DBG) {
-            MakedictLog.d("update parent address flags=" + flags + ", " + groupOriginAddress);
+            MakedictLog.d("update parent address flags=" + flags + ", " + ptNodeOriginAddress);
         }
-        final int parentOffset = newParentAddress - groupOriginAddress;
+        final int parentOffset = newParentAddress - ptNodeOriginAddress;
         BinaryDictIOUtils.writeSInt24ToBuffer(dictBuffer, parentOffset);
         dictBuffer.position(originalPosition);
     }
 
     /**
-     * Update parent addresses in a node array stored at nodeOriginAddress.
+     * Update parent addresses in a node array stored at ptNodeOriginAddress.
      *
      * @param dictBuffer the DictBuffer to be modified.
-     * @param nodeOriginAddress the address of the node array to update.
+     * @param ptNodeOriginAddress the address of the node array to update.
      * @param newParentAddress the address to be written.
      * @param formatOptions file format options.
      */
     public static void updateParentAddresses(final DictBuffer dictBuffer,
-            final int nodeOriginAddress, final int newParentAddress,
+            final int ptNodeOriginAddress, final int newParentAddress,
             final FormatOptions formatOptions) {
         final int originalPosition = dictBuffer.position();
-        dictBuffer.position(nodeOriginAddress);
+        dictBuffer.position(ptNodeOriginAddress);
         do {
-            final int count = BinaryDictDecoderUtils.readCharGroupCount(dictBuffer);
+            final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
             for (int i = 0; i < count; ++i) {
                 updateParentAddress(dictBuffer, dictBuffer.position(), newParentAddress,
                         formatOptions);
-                BinaryDictIOUtils.skipCharGroup(dictBuffer, formatOptions);
+                BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions);
             }
             final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
             dictBuffer.position(forwardLinkAddress);
@@ -129,18 +129,18 @@
     }
 
     /**
-     * Update a children address in a CharGroup that is addressed by groupOriginAddress.
+     * Update a children address in a PtNode that is addressed by ptNodeOriginAddress.
      *
      * @param dictBuffer the DictBuffer to write.
-     * @param groupOriginAddress the address of the group.
+     * @param ptNodeOriginAddress the address of the PtNode.
      * @param newChildrenAddress the absolute address of the child.
      * @param formatOptions file format options.
      */
     public static void updateChildrenAddress(final DictBuffer dictBuffer,
-            final int groupOriginAddress, final int newChildrenAddress,
+            final int ptNodeOriginAddress, final int newChildrenAddress,
             final FormatOptions formatOptions) {
         final int originalPosition = dictBuffer.position();
-        dictBuffer.position(groupOriginAddress);
+        dictBuffer.position(ptNodeOriginAddress);
         final int flags = dictBuffer.readUnsignedByte();
         final int parentAddress = BinaryDictDecoderUtils.readParentAddress(dictBuffer,
                 formatOptions);
@@ -153,21 +153,21 @@
     }
 
     /**
-     * Helper method to move a char group to the tail of the file.
+     * Helper method to move a PtNode to the tail of the file.
      */
-    private static int moveCharGroup(final OutputStream destination,
-            final DictBuffer dictBuffer, final CharGroupInfo info,
-            final int nodeArrayOriginAddress, final int oldGroupAddress,
+    private static int movePtNode(final OutputStream destination,
+            final DictBuffer dictBuffer, final PtNodeInfo info,
+            final int nodeArrayOriginAddress, final int oldNodeAddress,
             final FormatOptions formatOptions) throws IOException {
-        updateParentAddress(dictBuffer, oldGroupAddress, dictBuffer.limit() + 1, formatOptions);
-        dictBuffer.position(oldGroupAddress);
+        updateParentAddress(dictBuffer, oldNodeAddress, dictBuffer.limit() + 1, formatOptions);
+        dictBuffer.position(oldNodeAddress);
         final int currentFlags = dictBuffer.readUnsignedByte();
-        dictBuffer.position(oldGroupAddress);
+        dictBuffer.position(oldNodeAddress);
         dictBuffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags
                 & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG))));
-        int size = FormatSpec.GROUP_FLAGS_SIZE;
+        int size = FormatSpec.PTNODE_FLAGS_SIZE;
         updateForwardLink(dictBuffer, nodeArrayOriginAddress, dictBuffer.limit(), formatOptions);
-        size += BinaryDictIOUtils.writeNodes(destination, new CharGroupInfo[] { info });
+        size += BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { info });
         return size;
     }
 
@@ -178,9 +178,9 @@
         dictBuffer.position(nodeArrayOriginAddress);
         int jumpCount = 0;
         while (jumpCount++ < MAX_JUMPS) {
-            final int count = BinaryDictDecoderUtils.readCharGroupCount(dictBuffer);
+            final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
             for (int i = 0; i < count; ++i) {
-                BinaryDictIOUtils.skipCharGroup(dictBuffer, formatOptions);
+                BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions);
             }
             final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
             if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
@@ -196,43 +196,43 @@
     }
 
     /**
-     * Move a group that is referred to by oldGroupOrigin to the tail of the file, and set the
-     * children address to the byte after the group
+     * Move a PtNode that is referred to by oldPtNodeOrigin to the tail of the file, and set the
+     * children address to the byte after the PtNode.
      *
      * @param fileEndAddress the address of the tail of the file.
-     * @param codePoints the characters to put inside the group.
+     * @param codePoints the characters to put inside the PtNode.
      * @param length how many code points to read from codePoints.
-     * @param flags the flags for this group.
+     * @param flags the flags for this PtNode.
      * @param frequency the frequency of this terminal.
-     * @param parentAddress the address of the parent group of this group.
-     * @param shortcutTargets the shortcut targets for this group.
-     * @param bigrams the bigrams for this group.
+     * @param parentAddress the address of the parent PtNode of this PtNode.
+     * @param shortcutTargets the shortcut targets for this PtNode.
+     * @param bigrams the bigrams for this PtNode.
      * @param destination the stream representing the tail of the file.
      * @param dictBuffer the DictBuffer representing the (constant-size) body of the file.
-     * @param oldNodeArrayOrigin the origin of the old node array this group was a part of.
-     * @param oldGroupOrigin the old origin where this group used to be stored.
+     * @param oldPtNodeArrayOrigin the origin of the old PtNode array this PtNode was a part of.
+     * @param oldPtNodeOrigin the old origin where this PtNode used to be stored.
      * @param formatOptions format options for this dictionary.
      * @return the size written, in bytes.
      * @throws IOException if the file can't be accessed
      */
-    private static int moveGroup(final int fileEndAddress, final int[] codePoints,
+    private static int movePtNode(final int fileEndAddress, final int[] codePoints,
             final int length, final int flags, final int frequency, final int parentAddress,
             final ArrayList<WeightedString> shortcutTargets,
             final ArrayList<PendingAttribute> bigrams, final OutputStream destination,
-            final DictBuffer dictBuffer, final int oldNodeArrayOrigin,
-            final int oldGroupOrigin, final FormatOptions formatOptions) throws IOException {
+            final DictBuffer dictBuffer, final int oldPtNodeArrayOrigin,
+            final int oldPtNodeOrigin, final FormatOptions formatOptions) throws IOException {
         int size = 0;
-        final int newGroupOrigin = fileEndAddress + 1;
+        final int newPtNodeOrigin = fileEndAddress + 1;
         final int[] writtenCharacters = Arrays.copyOfRange(codePoints, 0, length);
-        final CharGroupInfo tmpInfo = new CharGroupInfo(newGroupOrigin, -1 /* endAddress */,
+        final PtNodeInfo tmpInfo = new PtNodeInfo(newPtNodeOrigin, -1 /* endAddress */,
                 flags, writtenCharacters, frequency, parentAddress, FormatSpec.NO_CHILDREN_ADDRESS,
                 shortcutTargets, bigrams);
-        size = BinaryDictIOUtils.computeGroupSize(tmpInfo, formatOptions);
-        final CharGroupInfo newInfo = new CharGroupInfo(newGroupOrigin, newGroupOrigin + size,
+        size = BinaryDictIOUtils.computePtNodeSize(tmpInfo, formatOptions);
+        final PtNodeInfo newInfo = new PtNodeInfo(newPtNodeOrigin, newPtNodeOrigin + size,
                 flags, writtenCharacters, frequency, parentAddress,
                 fileEndAddress + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets,
                 bigrams);
-        moveCharGroup(destination, dictBuffer, newInfo, oldNodeArrayOrigin, oldGroupOrigin,
+        movePtNode(destination, dictBuffer, newInfo, oldPtNodeArrayOrigin, oldPtNodeOrigin,
                 formatOptions);
         return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
     }
@@ -263,7 +263,7 @@
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
         if (bigramStrings != null) {
             for (final WeightedString bigram : bigramStrings) {
-                int position = BinaryDictIOUtils.getTerminalPosition(dictDecoder, bigram.mWord);
+                int position = dictDecoder.getTerminalPosition(bigram.mWord);
                 if (position == FormatSpec.NOT_VALID_WORD) {
                     // TODO: figure out what is the correct thing to do here.
                 } else {
@@ -288,16 +288,16 @@
             if (wordPos >= wordLen) break;
             nodeOriginAddress = dictBuffer.position();
             int nodeParentAddress = -1;
-            final int charGroupCount = BinaryDictDecoderUtils.readCharGroupCount(dictBuffer);
-            boolean foundNextGroup = false;
+            final int ptNodeCount = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+            boolean foundNextNode = false;
 
-            for (int i = 0; i < charGroupCount; ++i) {
+            for (int i = 0; i < ptNodeCount; ++i) {
                 address = dictBuffer.position();
-                final CharGroupInfo currentInfo = dictDecoder.readPtNode(address,
+                final PtNodeInfo currentInfo = dictDecoder.readPtNode(address,
                         fileHeader.mFormatOptions);
-                final boolean isMovedGroup = BinaryDictIOUtils.isMovedGroup(currentInfo.mFlags,
+                final boolean isMovedNode = BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags,
                         fileHeader.mFormatOptions);
-                if (isMovedGroup) continue;
+                if (isMovedNode) continue;
                 nodeParentAddress = (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS)
                         ? FormatSpec.NO_PARENT_ADDRESS : currentInfo.mParentAddress + address;
                 boolean matched = true;
@@ -314,10 +314,10 @@
                          *  abc - d - ef
                          */
                         final int newNodeAddress = dictBuffer.limit();
-                        final int flags = BinaryDictEncoderUtils.makeCharGroupFlags(p > 1,
+                        final int flags = BinaryDictEncoderUtils.makePtNodeFlags(p > 1,
                                 isTerminal, 0, hasShortcuts, hasBigrams, false /* isNotAWord */,
                                 false /* isBlackListEntry */, fileHeader.mFormatOptions);
-                        int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p, flags,
+                        int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p, flags,
                                 frequency, nodeParentAddress, shortcuts, bigrams, destination,
                                 dictBuffer, nodeOriginAddress, address, fileHeader.mFormatOptions);
 
@@ -327,12 +327,12 @@
                             updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress,
                                     newNodeAddress + written + 1, fileHeader.mFormatOptions);
                         }
-                        final CharGroupInfo newInfo2 = new CharGroupInfo(
+                        final PtNodeInfo newInfo2 = new PtNodeInfo(
                                 newNodeAddress + written + 1, -1 /* endAddress */,
                                 currentInfo.mFlags, characters2, currentInfo.mFrequency,
                                 newNodeAddress + 1, currentInfo.mChildrenAddress,
                                 currentInfo.mShortcutTargets, currentInfo.mBigrams);
-                        BinaryDictIOUtils.writeNodes(destination, new CharGroupInfo[] { newInfo2 });
+                        BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { newInfo2 });
                         return;
                     } else if (codePoints[wordPos + p] != currentInfo.mCharacters[p]) {
                         if (p > 0) {
@@ -353,12 +353,12 @@
                             final int childrenAddress = currentInfo.mChildrenAddress;
 
                             // move prefix
-                            final int prefixFlags = BinaryDictEncoderUtils.makeCharGroupFlags(p > 1,
+                            final int prefixFlags = BinaryDictEncoderUtils.makePtNodeFlags(p > 1,
                                     false /* isTerminal */, 0 /* childrenAddressSize*/,
                                     false /* hasShortcut */, false /* hasBigrams */,
                                     false /* isNotAWord */, false /* isBlackListEntry */,
                                     fileHeader.mFormatOptions);
-                            int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p,
+                            int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p,
                                     prefixFlags, -1 /* frequency */, nodeParentAddress, null, null,
                                     destination, dictBuffer, nodeOriginAddress, address,
                                     fileHeader.mFormatOptions);
@@ -369,7 +369,7 @@
                                 updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress,
                                         newNodeAddress + written + 1, fileHeader.mFormatOptions);
                             }
-                            final int suffixFlags = BinaryDictEncoderUtils.makeCharGroupFlags(
+                            final int suffixFlags = BinaryDictEncoderUtils.makePtNodeFlags(
                                     suffixCharacters.length > 1,
                                     (currentInfo.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0,
                                     0 /* childrenAddressSize */,
@@ -377,26 +377,26 @@
                                             != 0,
                                     (currentInfo.mFlags & FormatSpec.FLAG_HAS_BIGRAMS) != 0,
                                     isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
-                            final CharGroupInfo suffixInfo = new CharGroupInfo(
+                            final PtNodeInfo suffixInfo = new PtNodeInfo(
                                     newNodeAddress + written + 1, -1 /* endAddress */, suffixFlags,
                                     suffixCharacters, currentInfo.mFrequency, newNodeAddress + 1,
                                     currentInfo.mChildrenAddress, currentInfo.mShortcutTargets,
                                     currentInfo.mBigrams);
-                            written += BinaryDictIOUtils.computeGroupSize(suffixInfo,
+                            written += BinaryDictIOUtils.computePtNodeSize(suffixInfo,
                                     fileHeader.mFormatOptions) + 1;
 
                             final int[] newCharacters = Arrays.copyOfRange(codePoints, wordPos + p,
                                     codePoints.length);
-                            final int flags = BinaryDictEncoderUtils.makeCharGroupFlags(
+                            final int flags = BinaryDictEncoderUtils.makePtNodeFlags(
                                     newCharacters.length > 1, isTerminal,
                                     0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
                                     isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
-                            final CharGroupInfo newInfo = new CharGroupInfo(
+                            final PtNodeInfo newInfo = new PtNodeInfo(
                                     newNodeAddress + written, -1 /* endAddress */, flags,
                                     newCharacters, frequency, newNodeAddress + 1,
                                     FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
                             BinaryDictIOUtils.writeNodes(destination,
-                                    new CharGroupInfo[] { suffixInfo, newInfo });
+                                    new PtNodeInfo[] { suffixInfo, newInfo });
                             return;
                         }
                         matched = false;
@@ -407,17 +407,17 @@
                 if (matched) {
                     if (wordPos + currentInfo.mCharacters.length == wordLen) {
                         // the word exists in the dictionary.
-                        // only update group.
+                        // only update the PtNode.
                         final int newNodeAddress = dictBuffer.limit();
                         final boolean hasMultipleChars = currentInfo.mCharacters.length > 1;
-                        final int flags = BinaryDictEncoderUtils.makeCharGroupFlags(hasMultipleChars,
+                        final int flags = BinaryDictEncoderUtils.makePtNodeFlags(hasMultipleChars,
                                 isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
                                 isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
-                        final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
+                        final PtNodeInfo newInfo = new PtNodeInfo(newNodeAddress + 1,
                                 -1 /* endAddress */, flags, currentInfo.mCharacters, frequency,
                                 nodeParentAddress, currentInfo.mChildrenAddress, shortcuts,
                                 bigrams);
-                        moveCharGroup(destination, dictBuffer, newInfo, nodeOriginAddress, address,
+                        movePtNode(destination, dictBuffer, newInfo, nodeOriginAddress, address,
                                 fileHeader.mFormatOptions);
                         return;
                     }
@@ -425,7 +425,7 @@
                     if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
                         /*
                          * found the prefix of the word.
-                         * make new node and link to the node from this group.
+                         * make new PtNode and link to the PtNode from this PtNode.
                          *
                          * before
                          * ab - cd
@@ -435,28 +435,28 @@
                          * after
                          * ab - cd - e
                          */
-                        final int newNodeAddress = dictBuffer.limit();
-                        updateChildrenAddress(dictBuffer, address, newNodeAddress,
+                        final int newNodeArrayAddress = dictBuffer.limit();
+                        updateChildrenAddress(dictBuffer, address, newNodeArrayAddress,
                                 fileHeader.mFormatOptions);
-                        final int newGroupAddress = newNodeAddress + 1;
+                        final int newNodeAddress = newNodeArrayAddress + 1;
                         final boolean hasMultipleChars = (wordLen - wordPos) > 1;
-                        final int flags = BinaryDictEncoderUtils.makeCharGroupFlags(hasMultipleChars,
+                        final int flags = BinaryDictEncoderUtils.makePtNodeFlags(hasMultipleChars,
                                 isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
                                 isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
                         final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
-                        final CharGroupInfo newInfo = new CharGroupInfo(newGroupAddress, -1, flags,
+                        final PtNodeInfo newInfo = new PtNodeInfo(newNodeAddress, -1, flags,
                                 characters, frequency, address, FormatSpec.NO_CHILDREN_ADDRESS,
                                 shortcuts, bigrams);
-                        BinaryDictIOUtils.writeNodes(destination, new CharGroupInfo[] { newInfo });
+                        BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { newInfo });
                         return;
                     }
                     dictBuffer.position(currentInfo.mChildrenAddress);
-                    foundNextGroup = true;
+                    foundNextNode = true;
                     break;
                 }
             }
 
-            if (foundNextGroup) continue;
+            if (foundNextNode) continue;
 
             // reached the end of the array.
             final int linkAddressPosition = dictBuffer.position();
@@ -485,13 +485,13 @@
                 BinaryDictIOUtils.writeSInt24ToBuffer(dictBuffer, newNodeAddress);
 
                 final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
-                final int flags = BinaryDictEncoderUtils.makeCharGroupFlags(characters.length > 1,
+                final int flags = BinaryDictEncoderUtils.makePtNodeFlags(characters.length > 1,
                         isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
                         isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
-                final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
+                final PtNodeInfo newInfo = new PtNodeInfo(newNodeAddress + 1,
                         -1 /* endAddress */, flags, characters, frequency, nodeParentAddress,
                         FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
-                BinaryDictIOUtils.writeNodes(destination, new CharGroupInfo[]{ newInfo });
+                BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[]{ newInfo });
                 return;
             } else {
                 depth--;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 5e33121..b8ef576 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -62,19 +62,19 @@
     /*
      * Node array (FusionDictionary.PtNodeArray) layout is as follows:
      *
-     * g |
-     * r | the number of groups, 1 or 2 bytes.
-     * o | 1 byte = bbbbbbbb match
-     * u |   case 1xxxxxxx => xxxxxxx << 8 + next byte
-     * p |   otherwise => bbbbbbbb
-     * c |
-     * ount
+     * n |
+     * o | the number of PtNodes, 1 or 2 bytes.
+     * d | 1 byte = bbbbbbbb match
+     * e |   case 1xxxxxxx => xxxxxxx << 8 + next byte
+     * c |   otherwise => bbbbbbbb
+     * o |
+     * unt
      *
-     * g |
-     * r | sequence of groups,
-     * o | the layout of each group is described below.
-     * u |
-     * ps
+     * n |
+     * o | sequence of PtNodes,
+     * d | the layout of each PtNode is described below.
+     * e |
+     * s
      *
      * f |
      * o | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header)
@@ -86,19 +86,19 @@
      * linkaddress
      */
 
-    /* Node (FusionDictionary.CharGroup) layout is as follows:
+    /* Node (FusionDictionary.PtNode) layout is as follows:
      *   | IF !SUPPORTS_DYNAMIC_UPDATE
-     *   |   addressType                         xx     : mask with MASK_GROUP_ADDRESS_TYPE
-     *   |                           2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS
-     * f |                                   01 = 1 byte      : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE
-     * l |                                   10 = 2 bytes     : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
-     * a |                                   11 = 3 bytes     : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
+     *   |   addressType                    xx               : mask with MASK_CHILDREN_ADDRESS_TYPE
+     *   |                          2 bits, 00 = no children : FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS
+     * f |                                  01 = 1 byte      : FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE
+     * l |                                  10 = 2 bytes     : FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES
+     * a |                                  11 = 3 bytes     : FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
      * g | ELSE
-     * s |   is moved ?              2 bits, 11 = no          : FLAG_IS_NOT_MOVED
-     *   |                              This must be the same as FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
-     *   |                                   01 = yes         : FLAG_IS_MOVED
+     * s |   is moved ?             2 bits, 11 = no          : FLAG_IS_NOT_MOVED
+     *   |                            This must be the same as FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
+     *   |                                  01 = yes         : FLAG_IS_MOVED
      *   |                        the new address is stored in the same place as the parent address
-     *   |   is deleted?                     10 = yes         : FLAG_IS_DELETED
+     *   |   is deleted?                    10 = yes         : FLAG_IS_DELETED
      *   | has several chars ?         1 bit, 1 = yes, 0 = no   : FLAG_HAS_MULTIPLE_CHARS
      *   | has a terminal ?            1 bit, 1 = yes, 0 = no   : FLAG_IS_TERMINAL
      *   | has shortcut targets ?      1 bit, 1 = yes, 0 = no   : FLAG_HAS_SHORTCUT_TARGETS
@@ -116,7 +116,7 @@
      * ddress
      *
      * c | IF FLAG_HAS_MULTIPLE_CHARS
-     * h |   char, char, char, char    n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers
+     * h |   char, char, char, char    n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers
      * a |   end                       1 byte, = 0
      * r | ELSE
      * s |   char                      1 or 3 bytes
@@ -127,17 +127,22 @@
      * e |   frequency                 1 byte
      * q |
      *
-     * c | IF 00 = FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = addressType
-     * h |   // nothing
-     * i | ELSIF 01 = FLAG_GROUP_ADDRESS_TYPE_ONEBYTE == addressType
-     * l |   children address, 1 byte
-     * d | ELSIF 10 = FLAG_GROUP_ADDRESS_TYPE_TWOBYTES == addressType
-     * r |   children address, 2 bytes
-     * e | ELSE // 11 = FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = addressType
-     * n |   children address, 3 bytes
-     * A | END
-     * d
-     * dress
+     * c | IF SUPPORTS_DYNAMIC_UPDATE
+     * h |   children address, 3 bytes
+     * i |   1 byte = bbbbbbbb match
+     * l |     case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
+     * d |     otherwise => (bbbbbbbb<<16) + (next byte << 8) + next byte
+     * r | ELSIF 00 = FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS == addressType
+     * e |   // nothing
+     * n | ELSIF 01 = FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE == addressType
+     * A |   children address, 1 byte
+     * d | ELSIF 10 = FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES == addressType
+     * d |   children address, 2 bytes
+     * r | ELSE // 11 = FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = addressType
+     * e |   children address, 3 bytes
+     * s | END
+     * s
+     * ress
      *
      *   | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
      *   | shortcut string list
@@ -156,33 +161,33 @@
      * characters which should never happen anyway (and still work, but take 3 bytes).
      *
      * bigram address list is:
-     * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no     : FLAG_ATTRIBUTE_HAS_NEXT
-     *           | addressSign = 1 bit,                 : FLAG_ATTRIBUTE_OFFSET_NEGATIVE
+     * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no     : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
+     *           | addressSign = 1 bit,                 : FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE
      *           |                      1 = must take -address, 0 = must take +address
-     *           |                         xx : mask with MASK_ATTRIBUTE_ADDRESS_TYPE
-     *           | addressFormat = 2 bits, 00 = unused  : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
-     *           |                         01 = 1 byte  : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
-     *           |                         10 = 2 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES
-     *           |                         11 = 3 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES
-     *           | 4 bits : frequency         : mask with FLAG_ATTRIBUTE_FREQUENCY
-     * <address> | IF (01 == FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE == addressFormat)
+     *           |                         xx : mask with MASK_BIGRAM_ATTR_ADDRESS_TYPE
+     *           | addressFormat = 2 bits, 00 = unused  : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
+     *           |                         01 = 1 byte  : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
+     *           |                         10 = 2 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES
+     *           |                         11 = 3 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES
+     *           | 4 bits : frequency         : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
+     * <address> | IF (01 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE == addressFormat)
      *           |   read 1 byte, add top 4 bits
-     *           | ELSIF (10 == FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES == addressFormat)
+     *           | ELSIF (10 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES == addressFormat)
      *           |   read 2 bytes, add top 4 bits
-     *           | ELSE // 11 == FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES == addressFormat
+     *           | ELSE // 11 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES == addressFormat
      *           |   read 3 bytes, add top 4 bits
      *           | END
-     *           | if (FLAG_ATTRIBUTE_OFFSET_NEGATIVE) then address = -address
-     * if (FLAG_ATTRIBUTE_HAS_NEXT) goto bigram_and_shortcut_address_list_is
+     *           | if (FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) then address = -address
+     * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) goto bigram_and_shortcut_address_list_is
      *
      * shortcut string list is:
-     * <byte size> = GROUP_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
-     * <flags>     = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT
+     * <byte size> = PTNODE_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
+     * <flags>     = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
      *               | reserved = 3 bits, must be 0
-     *               | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY
+     *               | 4 bits : frequency : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
      * <shortcut>  = | string of characters at the char format described above, with the terminator
      *               | used to signal the end of the string.
-     * if (FLAG_ATTRIBUTE_HAS_NEXT goto flags
+     * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT goto flags
      */
 
     public static final int MAGIC_NUMBER = 0x9BC13AFE;
@@ -206,11 +211,11 @@
     static final int FORWARD_LINK_ADDRESS_SIZE = 3;
 
     // These flags are used only in the static dictionary.
-    static final int MASK_GROUP_ADDRESS_TYPE = 0xC0;
-    static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
-    static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
-    static final int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
-    static final int FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0;
+    static final int MASK_CHILDREN_ADDRESS_TYPE = 0xC0;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS = 0x00;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE = 0x40;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES = 0x80;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = 0xC0;
 
     static final int FLAG_HAS_MULTIPLE_CHARS = 0x20;
 
@@ -227,32 +232,32 @@
     static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
     static final int FLAG_IS_DELETED = 0x80;
 
-    static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
-    static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
-    static final int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30;
-    static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
-    static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
-    static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
-    static final int FLAG_ATTRIBUTE_FREQUENCY = 0x0F;
+    static final int FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT = 0x80;
+    static final int FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE = 0x40;
+    static final int MASK_BIGRAM_ATTR_ADDRESS_TYPE = 0x30;
+    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE = 0x10;
+    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES = 0x20;
+    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES = 0x30;
+    static final int FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY = 0x0F;
 
-    static final int GROUP_CHARACTERS_TERMINATOR = 0x1F;
+    static final int PTNODE_CHARACTERS_TERMINATOR = 0x1F;
 
-    static final int GROUP_TERMINATOR_SIZE = 1;
-    static final int GROUP_FLAGS_SIZE = 1;
-    static final int GROUP_FREQUENCY_SIZE = 1;
-    static final int GROUP_MAX_ADDRESS_SIZE = 3;
-    static final int GROUP_ATTRIBUTE_FLAGS_SIZE = 1;
-    static final int GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
-    static final int GROUP_SHORTCUT_LIST_SIZE_SIZE = 2;
+    static final int PTNODE_TERMINATOR_SIZE = 1;
+    static final int PTNODE_FLAGS_SIZE = 1;
+    static final int PTNODE_FREQUENCY_SIZE = 1;
+    static final int PTNODE_MAX_ADDRESS_SIZE = 3;
+    static final int PTNODE_ATTRIBUTE_FLAGS_SIZE = 1;
+    static final int PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
+    static final int PTNODE_SHORTCUT_LIST_SIZE_SIZE = 2;
 
     static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
     static final int NO_PARENT_ADDRESS = 0;
     static final int NO_FORWARD_LINK_ADDRESS = 0;
     static final int INVALID_CHARACTER = -1;
 
-    static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
-    static final int MAX_CHARGROUPS_IN_A_PT_NODE_ARRAY = 0x7FFF; // 32767
-    static final int MAX_BIGRAMS_IN_A_GROUP = 10000;
+    static final int MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT = 0x7F; // 127
+    static final int MAX_PTNODES_IN_A_PT_NODE_ARRAY = 0x7FFF; // 32767
+    static final int MAX_BIGRAMS_IN_A_PTNODE = 10000;
 
     static final int MAX_TERMINAL_FREQUENCY = 255;
     static final int MAX_BIGRAM_FREQUENCY = 15;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index fce1c5c..3e685a3 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -37,15 +37,15 @@
     private static int CHARACTER_NOT_FOUND_INDEX = -1;
 
     /**
-     * A node array of the dictionary, containing several CharGroups.
+     * A node array of the dictionary, containing several PtNodes.
      *
-     * A PtNodeArray is but an ordered array of CharGroups, which essentially contain all the
+     * A PtNodeArray is but an ordered array of PtNodes, which essentially contain all the
      * real information.
      * This class also contains fields to cache size and address, to help with binary
      * generation.
      */
     public static final class PtNodeArray {
-        ArrayList<CharGroup> mData;
+        ArrayList<PtNode> mData;
         // To help with binary generation
         int mCachedSize = Integer.MIN_VALUE;
         // mCachedAddressBefore/AfterUpdate are helpers for binary dictionary generation. They
@@ -58,9 +58,9 @@
         int mCachedParentAddress = 0;
 
         public PtNodeArray() {
-            mData = new ArrayList<CharGroup>();
+            mData = new ArrayList<PtNode>();
         }
-        public PtNodeArray(ArrayList<CharGroup> data) {
+        public PtNodeArray(ArrayList<PtNode> data) {
             mData = data;
         }
     }
@@ -93,18 +93,19 @@
     }
 
     /**
-     * A group of characters, with a frequency, shortcut targets, bigrams, and children.
+     * PtNode is a group of characters, with a frequency, shortcut targets, bigrams, and children
+     * (Pt means Patricia Trie).
      *
-     * This is the central class of the in-memory representation. A CharGroup is what can
+     * This is the central class of the in-memory representation. A PtNode is what can
      * be seen as a traditional "trie node", except it can hold several characters at the
-     * same time. A CharGroup essentially represents one or several characters in the middle
+     * same time. A PtNode essentially represents one or several characters in the middle
      * of the trie tree; as such, it can be a terminal, and it can have children.
-     * In this in-memory representation, whether the CharGroup is a terminal or not is represented
+     * In this in-memory representation, whether the PtNode is a terminal or not is represented
      * in the frequency, where NOT_A_TERMINAL (= -1) means this is not a terminal and any other
      * value is the frequency of this terminal. A terminal may have non-null shortcuts and/or
      * bigrams, but a non-terminal may not. Moreover, children, if present, are null.
      */
-    public static final class CharGroup {
+    public static final class PtNode {
         public static final int NOT_A_TERMINAL = -1;
         final int mChars[];
         ArrayList<WeightedString> mShortcutTargets;
@@ -119,11 +120,11 @@
         // same time. Updating will update the AfterUpdate value, and the code will move them
         // to BeforeUpdate before the next update pass.
         // The update process does not need two versions of mCachedSize.
-        int mCachedSize; // The size, in bytes, of this char group.
-        int mCachedAddressBeforeUpdate; // The address of this char group (before update)
-        int mCachedAddressAfterUpdate; // The address of this char group (after update)
+        int mCachedSize; // The size, in bytes, of this PtNode.
+        int mCachedAddressBeforeUpdate; // The address of this PtNode (before update)
+        int mCachedAddressAfterUpdate; // The address of this PtNode (after update)
 
-        public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
+        public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
                 final ArrayList<WeightedString> bigrams, final int frequency,
                 final boolean isNotAWord, final boolean isBlacklistEntry) {
             mChars = chars;
@@ -135,7 +136,7 @@
             mIsBlacklistEntry = isBlacklistEntry;
         }
 
-        public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
+        public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
                 final ArrayList<WeightedString> bigrams, final int frequency,
                 final boolean isNotAWord, final boolean isBlacklistEntry,
                 final PtNodeArray children) {
@@ -148,7 +149,7 @@
             mIsBlacklistEntry = isBlacklistEntry;
         }
 
-        public void addChild(CharGroup n) {
+        public void addChild(PtNode n) {
             if (null == mChildren) {
                 mChildren = new PtNodeArray();
             }
@@ -245,7 +246,7 @@
         }
 
         /**
-         * Updates the CharGroup with the given properties. Adds the shortcut and bigram lists to
+         * Updates the PtNode with the given properties. Adds the shortcut and bigram lists to
          * the existing ones if any. Note: unigram, bigram, and shortcut frequencies are only
          * updated if they are higher than the existing ones.
          */
@@ -407,13 +408,13 @@
     }
 
     /**
-     * Sanity check for a node array.
+     * Sanity check for a PtNode array.
      *
-     * This method checks that all CharGroups in a node array are ordered as expected.
+     * This method checks that all PtNodes in a node array are ordered as expected.
      * If they are, nothing happens. If they aren't, an exception is thrown.
      */
-    private void checkStack(PtNodeArray nodeArray) {
-        ArrayList<CharGroup> stack = nodeArray.mData;
+    private void checkStack(PtNodeArray ptNodeArray) {
+        ArrayList<PtNode> stack = ptNodeArray.mData;
         int lastValue = -1;
         for (int i = 0; i < stack.size(); ++i) {
             int currentValue = stack.get(i).mChars[0];
@@ -432,18 +433,18 @@
      * @param frequency the bigram frequency
      */
     public void setBigram(final String word1, final String word2, final int frequency) {
-        CharGroup charGroup = findWordInTree(mRootNodeArray, word1);
-        if (charGroup != null) {
-            final CharGroup charGroup2 = findWordInTree(mRootNodeArray, word2);
-            if (charGroup2 == null) {
+        PtNode ptNode = findWordInTree(mRootNodeArray, word1);
+        if (ptNode != null) {
+            final PtNode ptNode2 = findWordInTree(mRootNodeArray, word2);
+            if (ptNode2 == null) {
                 add(getCodePoints(word2), 0, null, false /* isNotAWord */,
                         false /* isBlacklistEntry */);
-                // The chargroup for the first word may have moved by the above insertion,
+                // The PtNode for the first word may have moved by the above insertion,
                 // if word1 and word2 share a common stem that happens not to have been
-                // a cutting point until now. In this case, we need to refresh charGroup.
-                charGroup = findWordInTree(mRootNodeArray, word1);
+                // a cutting point until now. In this case, we need to refresh ptNode.
+                ptNode = findWordInTree(mRootNodeArray, word1);
             }
-            charGroup.addBigram(word2, frequency);
+            ptNode.addBigram(word2, frequency);
         } else {
             throw new RuntimeException("First word of bigram not found");
         }
@@ -473,84 +474,83 @@
         PtNodeArray currentNodeArray = mRootNodeArray;
         int charIndex = 0;
 
-        CharGroup currentGroup = null;
+        PtNode currentPtNode = null;
         int differentCharIndex = 0; // Set by the loop to the index of the char that differs
         int nodeIndex = findIndexOfChar(mRootNodeArray, word[charIndex]);
         while (CHARACTER_NOT_FOUND_INDEX != nodeIndex) {
-            currentGroup = currentNodeArray.mData.get(nodeIndex);
-            differentCharIndex = compareCharArrays(currentGroup.mChars, word, charIndex);
+            currentPtNode = currentNodeArray.mData.get(nodeIndex);
+            differentCharIndex = compareCharArrays(currentPtNode.mChars, word, charIndex);
             if (ARRAYS_ARE_EQUAL != differentCharIndex
-                    && differentCharIndex < currentGroup.mChars.length) break;
-            if (null == currentGroup.mChildren) break;
-            charIndex += currentGroup.mChars.length;
+                    && differentCharIndex < currentPtNode.mChars.length) break;
+            if (null == currentPtNode.mChildren) break;
+            charIndex += currentPtNode.mChars.length;
             if (charIndex >= word.length) break;
-            currentNodeArray = currentGroup.mChildren;
+            currentNodeArray = currentPtNode.mChildren;
             nodeIndex = findIndexOfChar(currentNodeArray, word[charIndex]);
         }
 
         if (CHARACTER_NOT_FOUND_INDEX == nodeIndex) {
             // No node at this point to accept the word. Create one.
             final int insertionIndex = findInsertionIndex(currentNodeArray, word[charIndex]);
-            final CharGroup newGroup = new CharGroup(
-                    Arrays.copyOfRange(word, charIndex, word.length),
+            final PtNode newPtNode = new PtNode(Arrays.copyOfRange(word, charIndex, word.length),
                     shortcutTargets, null /* bigrams */, frequency, isNotAWord, isBlacklistEntry);
-            currentNodeArray.mData.add(insertionIndex, newGroup);
+            currentNodeArray.mData.add(insertionIndex, newPtNode);
             if (DBG) checkStack(currentNodeArray);
         } else {
             // There is a word with a common prefix.
-            if (differentCharIndex == currentGroup.mChars.length) {
+            if (differentCharIndex == currentPtNode.mChars.length) {
                 if (charIndex + differentCharIndex >= word.length) {
                     // The new word is a prefix of an existing word, but the node on which it
-                    // should end already exists as is. Since the old CharGroup was not a terminal,
+                    // should end already exists as is. Since the old PtNode was not a terminal,
                     // make it one by filling in its frequency and other attributes
-                    currentGroup.update(frequency, shortcutTargets, null, isNotAWord,
+                    currentPtNode.update(frequency, shortcutTargets, null, isNotAWord,
                             isBlacklistEntry);
                 } else {
                     // The new word matches the full old word and extends past it.
                     // We only have to create a new node and add it to the end of this.
-                    final CharGroup newNode = new CharGroup(
+                    final PtNode newNode = new PtNode(
                             Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length),
                                     shortcutTargets, null /* bigrams */, frequency, isNotAWord,
                                     isBlacklistEntry);
-                    currentGroup.mChildren = new PtNodeArray();
-                    currentGroup.mChildren.mData.add(newNode);
+                    currentPtNode.mChildren = new PtNodeArray();
+                    currentPtNode.mChildren.mData.add(newNode);
                 }
             } else {
                 if (0 == differentCharIndex) {
                     // Exact same word. Update the frequency if higher. This will also add the
                     // new shortcuts to the existing shortcut list if it already exists.
-                    currentGroup.update(frequency, shortcutTargets, null,
-                            currentGroup.mIsNotAWord && isNotAWord,
-                            currentGroup.mIsBlacklistEntry || isBlacklistEntry);
+                    currentPtNode.update(frequency, shortcutTargets, null,
+                            currentPtNode.mIsNotAWord && isNotAWord,
+                            currentPtNode.mIsBlacklistEntry || isBlacklistEntry);
                 } else {
                     // Partial prefix match only. We have to replace the current node with a node
                     // containing the current prefix and create two new ones for the tails.
                     PtNodeArray newChildren = new PtNodeArray();
-                    final CharGroup newOldWord = new CharGroup(
-                            Arrays.copyOfRange(currentGroup.mChars, differentCharIndex,
-                                    currentGroup.mChars.length), currentGroup.mShortcutTargets,
-                            currentGroup.mBigrams, currentGroup.mFrequency,
-                            currentGroup.mIsNotAWord, currentGroup.mIsBlacklistEntry,
-                            currentGroup.mChildren);
+                    final PtNode newOldWord = new PtNode(
+                            Arrays.copyOfRange(currentPtNode.mChars, differentCharIndex,
+                                    currentPtNode.mChars.length), currentPtNode.mShortcutTargets,
+                            currentPtNode.mBigrams, currentPtNode.mFrequency,
+                            currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry,
+                            currentPtNode.mChildren);
                     newChildren.mData.add(newOldWord);
 
-                    final CharGroup newParent;
+                    final PtNode newParent;
                     if (charIndex + differentCharIndex >= word.length) {
-                        newParent = new CharGroup(
-                                Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex),
+                        newParent = new PtNode(
+                                Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex),
                                 shortcutTargets, null /* bigrams */, frequency,
                                 isNotAWord, isBlacklistEntry, newChildren);
                     } else {
-                        newParent = new CharGroup(
-                                Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex),
+                        newParent = new PtNode(
+                                Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex),
                                 null /* shortcutTargets */, null /* bigrams */, -1,
                                 false /* isNotAWord */, false /* isBlacklistEntry */, newChildren);
-                        final CharGroup newWord = new CharGroup(Arrays.copyOfRange(word,
+                        final PtNode newWord = new PtNode(Arrays.copyOfRange(word,
                                 charIndex + differentCharIndex, word.length),
                                 shortcutTargets, null /* bigrams */, frequency,
                                 isNotAWord, isBlacklistEntry);
                         final int addIndex = word[charIndex + differentCharIndex]
-                                > currentGroup.mChars[differentCharIndex] ? 1 : 0;
+                                > currentPtNode.mChars[differentCharIndex] ? 1 : 0;
                         newChildren.mData.add(addIndex, newWord);
                     }
                     currentNodeArray.mData.set(nodeIndex, newParent);
@@ -589,29 +589,29 @@
     }
 
     /**
-     * Helper class that compares and sorts two chargroups according to their
+     * Helper class that compares and sorts two PtNodes according to their
      * first element only. I repeat: ONLY the first element is considered, the rest
      * is ignored.
      * This comparator imposes orderings that are inconsistent with equals.
      */
-    static private final class CharGroupComparator implements java.util.Comparator<CharGroup> {
+    static private final class PtNodeComparator implements java.util.Comparator<PtNode> {
         @Override
-        public int compare(CharGroup c1, CharGroup c2) {
-            if (c1.mChars[0] == c2.mChars[0]) return 0;
-            return c1.mChars[0] < c2.mChars[0] ? -1 : 1;
+        public int compare(PtNode p1, PtNode p2) {
+            if (p1.mChars[0] == p2.mChars[0]) return 0;
+            return p1.mChars[0] < p2.mChars[0] ? -1 : 1;
         }
     }
-    final static private CharGroupComparator CHARGROUP_COMPARATOR = new CharGroupComparator();
+    final static private PtNodeComparator PTNODE_COMPARATOR = new PtNodeComparator();
 
     /**
      * Finds the insertion index of a character within a node array.
      */
     private static int findInsertionIndex(final PtNodeArray nodeArray, int character) {
-        final ArrayList<CharGroup> data = nodeArray.mData;
-        final CharGroup reference = new CharGroup(new int[] { character },
+        final ArrayList<PtNode> data = nodeArray.mData;
+        final PtNode reference = new PtNode(new int[] { character },
                 null /* shortcutTargets */, null /* bigrams */, 0, false /* isNotAWord */,
                 false /* isBlacklistEntry */);
-        int result = Collections.binarySearch(data, reference, CHARGROUP_COMPARATOR);
+        int result = Collections.binarySearch(data, reference, PTNODE_COMPARATOR);
         return result >= 0 ? result : -result - 1;
     }
 
@@ -633,35 +633,37 @@
      * Helper method to find a word in a given branch.
      */
     @SuppressWarnings("unused")
-    public static CharGroup findWordInTree(PtNodeArray nodeArray, final String string) {
+    public static PtNode findWordInTree(PtNodeArray nodeArray, final String string) {
         int index = 0;
         final StringBuilder checker = DBG ? new StringBuilder() : null;
         final int[] codePoints = getCodePoints(string);
 
-        CharGroup currentGroup;
+        PtNode currentPtNode;
         do {
             int indexOfGroup = findIndexOfChar(nodeArray, codePoints[index]);
             if (CHARACTER_NOT_FOUND_INDEX == indexOfGroup) return null;
-            currentGroup = nodeArray.mData.get(indexOfGroup);
+            currentPtNode = nodeArray.mData.get(indexOfGroup);
 
-            if (codePoints.length - index < currentGroup.mChars.length) return null;
+            if (codePoints.length - index < currentPtNode.mChars.length) return null;
             int newIndex = index;
-            while (newIndex < codePoints.length && newIndex - index < currentGroup.mChars.length) {
-                if (currentGroup.mChars[newIndex - index] != codePoints[newIndex]) return null;
+            while (newIndex < codePoints.length && newIndex - index < currentPtNode.mChars.length) {
+                if (currentPtNode.mChars[newIndex - index] != codePoints[newIndex]) return null;
                 newIndex++;
             }
             index = newIndex;
 
-            if (DBG) checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length));
+            if (DBG) {
+                checker.append(new String(currentPtNode.mChars, 0, currentPtNode.mChars.length));
+            }
             if (index < codePoints.length) {
-                nodeArray = currentGroup.mChildren;
+                nodeArray = currentPtNode.mChildren;
             }
         } while (null != nodeArray && index < codePoints.length);
 
         if (index < codePoints.length) return null;
-        if (!currentGroup.isTerminal()) return null;
+        if (!currentPtNode.isTerminal()) return null;
         if (DBG && !string.equals(checker.toString())) return null;
-        return currentGroup;
+        return currentPtNode;
     }
 
     /**
@@ -675,18 +677,18 @@
     }
 
     /**
-     * Recursively count the number of character groups in a given branch of the trie.
+     * Recursively count the number of PtNodes in a given branch of the trie.
      *
      * @param nodeArray the parent node.
-     * @return the number of char groups in all the branch under this node.
+     * @return the number of PtNodes in all the branch under this node.
      */
-    public static int countCharGroups(final PtNodeArray nodeArray) {
+    public static int countPtNodes(final PtNodeArray nodeArray) {
         final int nodeSize = nodeArray.mData.size();
         int size = nodeSize;
         for (int i = nodeSize - 1; i >= 0; --i) {
-            CharGroup group = nodeArray.mData.get(i);
-            if (null != group.mChildren)
-                size += countCharGroups(group.mChildren);
+            PtNode ptNode = nodeArray.mData.get(i);
+            if (null != ptNode.mChildren)
+                size += countPtNodes(ptNode.mChildren);
         }
         return size;
     }
@@ -700,9 +702,9 @@
     public static int countNodeArrays(final PtNodeArray nodeArray) {
         int size = 1;
         for (int i = nodeArray.mData.size() - 1; i >= 0; --i) {
-            CharGroup group = nodeArray.mData.get(i);
-            if (null != group.mChildren)
-                size += countNodeArrays(group.mChildren);
+            PtNode ptNode = nodeArray.mData.get(i);
+            if (null != ptNode.mChildren)
+                size += countNodeArrays(ptNode.mChildren);
         }
         return size;
     }
@@ -713,9 +715,9 @@
     private static boolean hasBigramsInternal(final PtNodeArray nodeArray) {
         if (null == nodeArray) return false;
         for (int i = nodeArray.mData.size() - 1; i >= 0; --i) {
-            CharGroup group = nodeArray.mData.get(i);
-            if (null != group.mBigrams) return true;
-            if (hasBigramsInternal(group.mChildren)) return true;
+            PtNode ptNode = nodeArray.mData.get(i);
+            if (null != ptNode.mBigrams) return true;
+            if (hasBigramsInternal(ptNode.mChildren)) return true;
         }
         return false;
     }
@@ -748,8 +750,8 @@
         MakedictLog.i("Do not merge tails");
         return;
 
-//        MakedictLog.i("Merging nodes. Number of nodes : " + countNodes(root));
-//        MakedictLog.i("Number of groups : " + countCharGroups(root));
+//        MakedictLog.i("Merging PtNodes. Number of PtNodes : " + countPtNodes(root));
+//        MakedictLog.i("Number of PtNodes : " + countPtNodes(root));
 //
 //        final HashMap<String, ArrayList<PtNodeArray>> repository =
 //                  new HashMap<String, ArrayList<PtNodeArray>>();
@@ -771,25 +773,25 @@
 //       if (a.data.size() != b.data.size()) return false;
 //       final int size = a.data.size();
 //       for (int i = size - 1; i >= 0; --i) {
-//           CharGroup aGroup = a.data.get(i);
-//           CharGroup bGroup = b.data.get(i);
-//           if (aGroup.frequency != bGroup.frequency) return false;
-//           if (aGroup.alternates == null && bGroup.alternates != null) return false;
-//           if (aGroup.alternates != null && !aGroup.equals(bGroup.alternates)) return false;
-//           if (!Arrays.equals(aGroup.chars, bGroup.chars)) return false;
-//           if (!isEqual(aGroup.children, bGroup.children)) return false;
+//           PtNode aPtNode = a.data.get(i);
+//           PtNode bPtNode = b.data.get(i);
+//           if (aPtNode.frequency != bPtNode.frequency) return false;
+//           if (aPtNode.alternates == null && bPtNode.alternates != null) return false;
+//           if (aPtNode.alternates != null && !aPtNode.equals(bPtNode.alternates)) return false;
+//           if (!Arrays.equals(aPtNode.chars, bPtNode.chars)) return false;
+//           if (!isEqual(aPtNode.children, bPtNode.children)) return false;
 //       }
 //       return true;
 //   }
 
 //   static private HashMap<String, ArrayList<PtNodeArray>> mergeTailsInner(
 //           final HashMap<String, ArrayList<PtNodeArray>> map, final PtNodeArray nodeArray) {
-//       final ArrayList<CharGroup> branches = nodeArray.data;
+//       final ArrayList<PtNode> branches = nodeArray.data;
 //       final int nodeSize = branches.size();
 //       for (int i = 0; i < nodeSize; ++i) {
-//           CharGroup group = branches.get(i);
-//           if (null != group.children) {
-//               String pseudoHash = getPseudoHash(group.children);
+//           PtNode ptNode = branches.get(i);
+//           if (null != ptNode.children) {
+//               String pseudoHash = getPseudoHash(ptNode.children);
 //               ArrayList<PtNodeArray> similarList = map.get(pseudoHash);
 //               if (null == similarList) {
 //                   similarList = new ArrayList<PtNodeArray>();
@@ -797,16 +799,16 @@
 //               }
 //               boolean merged = false;
 //               for (PtNodeArray similar : similarList) {
-//                   if (isEqual(group.children, similar)) {
-//                       group.children = similar;
+//                   if (isEqual(ptNode.children, similar)) {
+//                       ptNode.children = similar;
 //                       merged = true;
 //                       break;
 //                   }
 //               }
 //               if (!merged) {
-//                   similarList.add(group.children);
+//                   similarList.add(ptNode.children);
 //               }
-//               mergeTailsInner(map, group.children);
+//               mergeTailsInner(map, ptNode.children);
 //           }
 //       }
 //       return map;
@@ -814,9 +816,9 @@
 
 //  private static String getPseudoHash(final PtNodeArray nodeArray) {
 //      StringBuilder s = new StringBuilder();
-//      for (CharGroup g : nodeArray.data) {
-//          s.append(g.frequency);
-//          for (int ch : g.chars) {
+//      for (PtNode ptNode : nodeArray.data) {
+//          s.append(ptNode.frequency);
+//          for (int ch : ptNode.chars) {
 //              s.append(Character.toChars(ch));
 //          }
 //      }
@@ -830,20 +832,20 @@
      */
     public static final class DictionaryIterator implements Iterator<Word> {
         private static final class Position {
-            public Iterator<CharGroup> pos;
+            public Iterator<PtNode> pos;
             public int length;
-            public Position(ArrayList<CharGroup> groups) {
-                pos = groups.iterator();
+            public Position(ArrayList<PtNode> ptNodes) {
+                pos = ptNodes.iterator();
                 length = 0;
             }
         }
         final StringBuilder mCurrentString;
         final LinkedList<Position> mPositions;
 
-        public DictionaryIterator(ArrayList<CharGroup> root) {
+        public DictionaryIterator(ArrayList<PtNode> ptRoot) {
             mCurrentString = new StringBuilder();
             mPositions = new LinkedList<Position>();
-            final Position rootPos = new Position(root);
+            final Position rootPos = new Position(ptRoot);
             mPositions.add(rootPos);
         }
 
@@ -864,20 +866,20 @@
 
             do {
                 if (currentPos.pos.hasNext()) {
-                    final CharGroup currentGroup = currentPos.pos.next();
+                    final PtNode currentPtNode = currentPos.pos.next();
                     currentPos.length = mCurrentString.length();
-                    for (int i : currentGroup.mChars) {
+                    for (int i : currentPtNode.mChars) {
                         mCurrentString.append(Character.toChars(i));
                     }
-                    if (null != currentGroup.mChildren) {
-                        currentPos = new Position(currentGroup.mChildren.mData);
+                    if (null != currentPtNode.mChildren) {
+                        currentPos = new Position(currentPtNode.mChildren.mData);
                         currentPos.length = mCurrentString.length();
                         mPositions.addLast(currentPos);
                     }
-                    if (currentGroup.mFrequency >= 0) {
-                        return new Word(mCurrentString.toString(), currentGroup.mFrequency,
-                                currentGroup.mShortcutTargets, currentGroup.mBigrams,
-                                currentGroup.mIsNotAWord, currentGroup.mIsBlacklistEntry);
+                    if (currentPtNode.mFrequency >= 0) {
+                        return new Word(mCurrentString.toString(), currentPtNode.mFrequency,
+                                currentPtNode.mShortcutTargets, currentPtNode.mBigrams,
+                                currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry);
                     }
                 } else {
                     mPositions.removeLast();
diff --git a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
similarity index 88%
rename from java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
rename to java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
index b361744..188de7a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
+++ b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
@@ -21,9 +21,9 @@
 import java.util.ArrayList;
 
 /**
- * Raw char group info straight out of a file. This will contain numbers for addresses.
+ * Raw PtNode info straight out of a file. This will contain numbers for addresses.
  */
-public final class CharGroupInfo {
+public final class PtNodeInfo {
 
     public final int mOriginalAddress;
     public final int mEndAddress;
@@ -35,7 +35,7 @@
     public final ArrayList<WeightedString> mShortcutTargets;
     public final ArrayList<PendingAttribute> mBigrams;
 
-    public CharGroupInfo(final int originalAddress, final int endAddress, final int flags,
+    public PtNodeInfo(final int originalAddress, final int endAddress, final int flags,
             final int[] characters, final int frequency, final int parentAddress,
             final int childrenAddress, final ArrayList<WeightedString> shortcutTargets,
             final ArrayList<PendingAttribute> bigrams) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
index f655e30..77e6393 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
@@ -21,7 +21,7 @@
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.utils.JniUtils;
 
@@ -99,14 +99,14 @@
                 if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
                 return address;
             } else {
-                switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
-                    case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
+                switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
                         return dictBuffer.readUnsignedByte();
-                    case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
                         return dictBuffer.readUnsignedShort();
-                    case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
                         return dictBuffer.readUnsignedInt24();
-                    case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
                     default:
                         return FormatSpec.NO_CHILDREN_ADDRESS;
                 }
@@ -122,8 +122,8 @@
                 final int targetFlags = dictBuffer.readUnsignedByte();
                 final String word = CharEncoding.readString(dictBuffer);
                 shortcutTargets.add(new WeightedString(word,
-                        targetFlags & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY));
-                if (0 == (targetFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
+                        targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
+                if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
             }
             return dictBuffer.position() - pointerBefore;
         }
@@ -132,22 +132,22 @@
                 final ArrayList<PendingAttribute> bigrams, final int baseAddress) {
             int readLength = 0;
             int bigramCount = 0;
-            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
                 final int bigramFlags = dictBuffer.readUnsignedByte();
                 ++readLength;
-                final int sign = 0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE)
+                final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE)
                         ? 1 : -1;
                 int bigramAddress = baseAddress + readLength;
-                switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
+                switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
                         bigramAddress += sign * dictBuffer.readUnsignedByte();
                         readLength += 1;
                         break;
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
                         bigramAddress += sign * dictBuffer.readUnsignedShort();
                         readLength += 2;
                         break;
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
                         final int offset = (dictBuffer.readUnsignedByte() << 16)
                                 + dictBuffer.readUnsignedShort();
                         bigramAddress += sign * offset;
@@ -156,9 +156,10 @@
                     default:
                         throw new RuntimeException("Has bigrams with no address");
                 }
-                bigrams.add(new PendingAttribute(bigramFlags & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY,
+                bigrams.add(new PendingAttribute(
+                        bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY,
                         bigramAddress));
-                if (0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
+                if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
             }
             return readLength;
         }
@@ -236,7 +237,7 @@
     // TODO: Make this buffer multi thread safe.
     private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
     @Override
-    public CharGroupInfo readPtNode(final int ptNodePos, final FormatOptions options) {
+    public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) {
         int addressPointer = ptNodePos;
         final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
         ++addressPointer;
@@ -270,7 +271,7 @@
             ++addressPointer;
             frequency = PtNodeReader.readFrequency(mDictBuffer);
         } else {
-            frequency = CharGroup.NOT_A_TERMINAL;
+            frequency = PtNode.NOT_A_TERMINAL;
         }
         int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options);
         if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
@@ -290,14 +291,13 @@
         if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
             bigrams = new ArrayList<PendingAttribute>();
             addressPointer += PtNodeReader.readBigrams(mDictBuffer, bigrams, addressPointer);
-            if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
-                MakedictLog.d("too many bigrams in a group.");
+            if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+                MakedictLog.d("too many bigrams in a PtNode.");
             }
         } else {
             bigrams = null;
         }
-
-        return new CharGroupInfo(ptNodePos, addressPointer, flags, characters, frequency,
+        return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency,
                 parentAddress, childrenAddress, shortcutTargets, bigrams);
     }
 
@@ -309,4 +309,12 @@
         }
         return BinaryDictDecoderUtils.readDictionaryBinary(this, dict);
     }
+
+    @Override
+    public int getTerminalPosition(String word) throws IOException, UnsupportedFormatException {
+        if (mDictBuffer == null) {
+            openDictBuffer();
+        }
+        return BinaryDictIOUtils.getTerminalPosition(this, word);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
new file mode 100644
index 0000000..a1d93ef
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.android.inputmethod.latin.personalization;
+
+import android.content.Context;
+
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.AbstractDictionaryWriter;
+import com.android.inputmethod.latin.ExpandableDictionary;
+import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.ExpandableDictionary.NextWord;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.makedict.DictEncoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+// Currently this class is used to implement dynamic prodiction dictionary.
+// TODO: Move to native code.
+public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWriter {
+    private static final String TAG = DynamicPersonalizationDictionaryWriter.class.getSimpleName();
+    /** Maximum number of pairs. Pruning will start when databases goes above this number. */
+    public static final int MAX_HISTORY_BIGRAMS = 10000;
+
+    /** Any pair being typed or picked */
+    private static final int FREQUENCY_FOR_TYPED = 2;
+
+    private static final int BINARY_DICT_VERSION = 3;
+    private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
+            new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */);
+
+    private final UserHistoryDictionaryBigramList mBigramList =
+            new UserHistoryDictionaryBigramList();
+    private final ExpandableDictionary mExpandableDictionary;
+
+    public DynamicPersonalizationDictionaryWriter(final Context context, final String dictType) {
+        super(context, dictType);
+        mExpandableDictionary = new ExpandableDictionary(context, dictType);
+    }
+
+    @Override
+    public void clear() {
+        mBigramList.evictAll();
+        mExpandableDictionary.clearDictionary();
+    }
+
+    /**
+     * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes
+     * are done to update the binary dictionary.
+     */
+    @Override
+    public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
+            final boolean isNotAWord) {
+        mExpandableDictionary.addWord(word, shortcutTarget, frequency);
+        mBigramList.addBigram(null, word, (byte)frequency);
+    }
+
+    @Override
+    public void addBigramWords(final String word0, final String word1, final int frequency,
+            final boolean isValid, final long lastModifiedTime) {
+        if (lastModifiedTime > 0) {
+            mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
+                    new ForgettingCurveParams(frequency, System.currentTimeMillis(),
+                            lastModifiedTime));
+            mBigramList.addBigram(word0, word1, (byte)frequency);
+        } else {
+            mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
+                    new ForgettingCurveParams(isValid));
+            mBigramList.addBigram(word0, word1, (byte)frequency);
+        }
+    }
+
+    @Override
+    public void removeBigramWords(final String word0, final String word1) {
+        if (mBigramList.removeBigram(word0, word1)) {
+            mExpandableDictionary.removeBigram(word0, word1);
+        }
+    }
+
+    @Override
+    protected void writeDictionary(final DictEncoder dictEncoder)
+            throws IOException, UnsupportedFormatException {
+        UserHistoryDictIOUtils.writeDictionary(dictEncoder,
+                new FrequencyProvider(mBigramList, mExpandableDictionary), mBigramList,
+                        FORMAT_OPTIONS);
+    }
+
+    private static class FrequencyProvider implements BigramDictionaryInterface {
+        final private UserHistoryDictionaryBigramList mBigramList;
+        final private ExpandableDictionary mExpandableDictionary;
+
+        public FrequencyProvider(final UserHistoryDictionaryBigramList bigramList,
+                final ExpandableDictionary expandableDictionary) {
+            mBigramList = bigramList;
+            mExpandableDictionary = expandableDictionary;
+        }
+        @Override
+        public int getFrequency(final String word0, final String word1) {
+            final int freq;
+            if (word0 == null) { // unigram
+                freq = FREQUENCY_FOR_TYPED;
+            } else { // bigram
+                final NextWord nw = mExpandableDictionary.getBigramWord(word0, word1);
+                if (nw != null) {
+                    final ForgettingCurveParams forgettingCurveParams = nw.getFcParams();
+                    final byte prevFc = mBigramList.getBigrams(word0).get(word1);
+                    final byte fc = forgettingCurveParams.getFc();
+                    final boolean isValid = forgettingCurveParams.isValid();
+                    if (prevFc > 0 && prevFc == fc) {
+                        freq = fc & 0xFF;
+                    } else if (UserHistoryForgettingCurveUtils.
+                            needsToSave(fc, isValid, mBigramList.size() <= MAX_HISTORY_BIGRAMS)) {
+                        freq = fc & 0xFF;
+                    } else {
+                        // Delete this entry
+                        freq = -1;
+                    }
+                } else {
+                    // Delete this entry
+                    freq = -1;
+                }
+            }
+            return freq;
+        }
+    }
+
+    @Override
+    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+            final String prevWord, final ProximityInfo proximityInfo,
+            boolean blockOffensiveWords) {
+        return mExpandableDictionary.getSuggestions(composer, prevWord, proximityInfo,
+                blockOffensiveWords);
+    }
+
+    @Override
+    public boolean isValidWord(final String word) {
+        return mExpandableDictionary.isValidWord(word);
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 3a34082..da9c611 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -1427,7 +1427,7 @@
                 kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey,
                 isPasswordView, kid.mShortcutKeyEnabled, kid.mHasShortcutKey,
                 kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth,
-                keyboard.mOccupiedHeight, keyboard.mKeys);
+                keyboard.mOccupiedHeight, keyboard.getKeys());
     }
 
     /**
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
index 11da039..89e0cd4 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
@@ -39,7 +39,7 @@
     int nextPos = dicNode->getChildrenPos();
     int totalChildCount = 0;
     do {
-        const int childCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(
+        const int childCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
                 mDictRoot, &nextPos);
         totalChildCount += childCount;
         if (childCount <= 0 || totalChildCount > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP) {
@@ -131,7 +131,7 @@
         bool foundMatchedNode = false;
         int totalChildCount = 0;
         do {
-            const int childCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(
+            const int childCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
                     mDictRoot, &pos);
             totalChildCount += childCount;
             if (childCount <= 0 || totalChildCount > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP) {
@@ -179,7 +179,7 @@
             if (foundMatchedNode) {
                 break;
             }
-            // If the matched node is not found in the current node group, try to follow the
+            // If the matched node is not found in the current PtNode array, try to follow the
             // forward link.
             pos = DynamicPatriciaTrieReadingUtils::getForwardLinkPosition(
                     mDictRoot, pos);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
index 15eb067..adcf2db 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
@@ -30,7 +30,7 @@
         return;
     }
     int nextPos = dicNode->getChildrenPos();
-    const int childCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(
+    const int childCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
             mDictRoot, &nextPos);
     for (int i = 0; i < childCount; i++) {
         nextPos = createAndGetLeavingChildNode(dicNode, nextPos, childDicNodes);
@@ -40,15 +40,15 @@
 // This retrieves code points and the probability of the word by its terminal position.
 // Due to the fact that words are ordered in the dictionary in a strict breadth-first order,
 // it is possible to check for this with advantageous complexity. For each node, we search
-// for groups with children and compare the children position with the position we look for.
+// for PtNodes with children and compare the children position with the position we look for.
 // When we shoot the position we look for, it means the word we look for is in the children
-// of the previous group. The only tricky part is the fact that if we arrive at the end of a
-// node with the last group's children position still less than what we are searching for, we
-// must descend the last group's children (for example, if the word we are searching for starts
-// with a z, it's the last group of the root node, so all children addresses will be smaller
+// of the previous PtNode. The only tricky part is the fact that if we arrive at the end of a
+// PtNode array with the last PtNode's children position still less than what we are searching for,
+// we must descend the last PtNode's children (for example, if the word we are searching for starts
+// with a z, it's the last PtNode of the root array, so all children addresses will be smaller
 // than the position we look for, and we have to descend the z node).
 /* Parameters :
- * nodePos: the byte position of the terminal chargroup of the word we are searching for (this is
+ * nodePos: the byte position of the terminal PtNode of the word we are searching for (this is
  *   what is stored as the "bigram position" in each bigram)
  * outCodePoints: an array to write the found word, with MAX_WORD_LENGTH size.
  * outUnigramProbability: a pointer to an int to write the probability into.
@@ -60,18 +60,18 @@
         int *const outUnigramProbability) const {
     int pos = getRootPosition();
     int wordPos = 0;
-    // One iteration of the outer loop iterates through nodes. As stated above, we will only
-    // traverse nodes that are actually a part of the terminal we are searching, so each time
+    // One iteration of the outer loop iterates through PtNode arrays. As stated above, we will
+    // only traverse nodes that are actually a part of the terminal we are searching, so each time
     // we enter this loop we are one depth level further than last time.
     // The only reason we count nodes is because we want to reduce the probability of infinite
     // looping in case there is a bug. Since we know there is an upper bound to the depth we are
     // supposed to traverse, it does not hurt to count iterations.
     for (int loopCount = maxCodePointCount; loopCount > 0; --loopCount) {
-        int lastCandidateGroupPos = 0;
-        // Let's loop through char groups in this node searching for either the terminal
+        int lastCandidatePtNodePos = 0;
+        // Let's loop through PtNodes in this PtNode array searching for either the terminal
         // or one of its ascendants.
-        for (int charGroupCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(
-                mDictRoot, &pos); charGroupCount > 0; --charGroupCount) {
+        for (int ptNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
+                mDictRoot, &pos); ptNodeCount > 0; --ptNodeCount) {
             const int startPos = pos;
             const PatriciaTrieReadingUtils::NodeFlags flags =
                     PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
@@ -98,7 +98,7 @@
                                 &pos);
                 return ++wordPos;
             }
-            // We need to skip past this char group, so skip any remaining code points after the
+            // We need to skip past this PtNode, so skip any remaining code points after the
             // first and possibly the probability.
             if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) {
                 PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos);
@@ -106,8 +106,8 @@
             if (PatriciaTrieReadingUtils::isTerminal(flags)) {
                 PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
             }
-            // The fact that this group has children is very important. Since we already know
-            // that this group does not match, if it has no children we know it is irrelevant
+            // The fact that this PtNode has children is very important. Since we already know
+            // that this PtNode does not match, if it has no children we know it is irrelevant
             // to what we are searching for.
             const bool hasChildren = PatriciaTrieReadingUtils::hasChildrenInFlags(flags);
             // We will write in `found' whether we have passed the children position we are
@@ -122,45 +122,45 @@
                         ::readChildrenPositionAndAdvancePosition(mDictRoot, flags, &currentPos);
                 if (childrenPos > nodePos) {
                     // If the children pos is greater than the position, it means the previous
-                    // chargroup, which position is stored in lastCandidateGroupPos, was the right
+                    // PtNode, which position is stored in lastCandidatePtNodePos, was the right
                     // one.
                     found = true;
-                } else if (1 >= charGroupCount) {
-                    // However if we are on the LAST group of this node, and we have NOT shot the
-                    // position we should descend THIS node. So we trick the lastCandidateGroupPos
-                    // so that we will descend this node, not the previous one.
-                    lastCandidateGroupPos = startPos;
+                } else if (1 >= ptNodeCount) {
+                    // However if we are on the LAST PtNode of this array, and we have NOT shot the
+                    // position we should descend THIS node. So we trick the lastCandidatePtNodePos
+                    // so that we will descend this PtNode, not the previous one.
+                    lastCandidatePtNodePos = startPos;
                     found = true;
                 } else {
                     // Else, we should continue looking.
                     found = false;
                 }
             } else {
-                // Even if we don't have children here, we could still be on the last group of this
-                // node. If this is the case, we should descend the last group that had children,
-                // and their position is already in lastCandidateGroup.
-                found = (1 >= charGroupCount);
+                // Even if we don't have children here, we could still be on the last PtNode of /
+                // this array. If this is the case, we should descend the last PtNode that had
+                // children, and their position is already in lastCandidatePtNodePos.
+                found = (1 >= ptNodeCount);
             }
 
             if (found) {
-                // Okay, we found the group we should descend. Its position is in
-                // the lastCandidateGroupPos variable, so we just re-read it.
-                if (0 != lastCandidateGroupPos) {
+                // Okay, we found the PtNode we should descend. Its position is in
+                // the lastCandidatePtNodePos variable, so we just re-read it.
+                if (0 != lastCandidatePtNodePos) {
                     const PatriciaTrieReadingUtils::NodeFlags lastFlags =
                             PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(
-                                    mDictRoot, &lastCandidateGroupPos);
+                                    mDictRoot, &lastCandidatePtNodePos);
                     const int lastChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
-                            mDictRoot, &lastCandidateGroupPos);
-                    // We copy all the characters in this group to the buffer
+                            mDictRoot, &lastCandidatePtNodePos);
+                    // We copy all the characters in this PtNode to the buffer
                     outCodePoints[wordPos] = lastChar;
                     if (PatriciaTrieReadingUtils::hasMultipleChars(lastFlags)) {
                         int nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
-                                mDictRoot, &lastCandidateGroupPos);
+                                mDictRoot, &lastCandidatePtNodePos);
                         int charCount = maxCodePointCount;
                         while (-1 != nextChar && --charCount > 0) {
                             outCodePoints[++wordPos] = nextChar;
                             nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
-                                    mDictRoot, &lastCandidateGroupPos);
+                                    mDictRoot, &lastCandidatePtNodePos);
                         }
                     }
                     ++wordPos;
@@ -168,19 +168,19 @@
                     // it's there, read pos, and break to resume the search at pos.
                     if (PatriciaTrieReadingUtils::isTerminal(lastFlags)) {
                         PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot,
-                                &lastCandidateGroupPos);
+                                &lastCandidatePtNodePos);
                     }
                     pos = PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
-                            mDictRoot, lastFlags, &lastCandidateGroupPos);
+                            mDictRoot, lastFlags, &lastCandidatePtNodePos);
                     break;
                 } else {
                     // Here is a little tricky part: we come here if we found out that all children
-                    // addresses in this group are bigger than the address we are searching for.
+                    // addresses in this PtNode are bigger than the address we are searching for.
                     // Should we conclude the word is not in the dictionary? No! It could still be
-                    // one of the remaining chargroups in this node, so we have to keep looking in
-                    // this node until we find it (or we realize it's not there either, in which
-                    // case it's actually not in the dictionary). Pass the end of this group, ready
-                    // to start the next one.
+                    // one of the remaining PtNodes in this array, so we have to keep looking in
+                    // this array until we find it (or we realize it's not there either, in which
+                    // case it's actually not in the dictionary). Pass the end of this PtNode,
+                    // ready to start the next one.
                     if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
                         PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
                                 mDictRoot, flags, &pos);
@@ -195,9 +195,9 @@
             } else {
                 // If we did not find it, we should record the last children address for the next
                 // iteration.
-                if (hasChildren) lastCandidateGroupPos = startPos;
-                // Now skip the end of this group (children pos and the attributes if any) so that
-                // our pos is after the end of this char group, at the start of the next one.
+                if (hasChildren) lastCandidatePtNodePos = startPos;
+                // Now skip the end of this PtNode (children pos and the attributes if any) so that
+                // our pos is after the end of this PtNode, at the start of the next one.
                 if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
                     PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
                             mDictRoot, flags, &pos);
@@ -212,7 +212,7 @@
 
         }
     }
-    // If we have looked through all the chargroups and found no match, the nodePos is
+    // If we have looked through all the PtNodes and found no match, the nodePos is
     // not the position of a terminal in this dictionary.
     return 0;
 }
@@ -228,24 +228,24 @@
         // If we already traversed the tree further than the word is long, there means
         // there was no match (or we would have found it).
         if (wordPos >= length) return NOT_A_VALID_WORD_POS;
-        int charGroupCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(mDictRoot,
+        int ptNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(mDictRoot,
                 &pos);
         const int wChar = forceLowerCaseSearch
                 ? CharUtils::toLowerCase(inWord[wordPos]) : inWord[wordPos];
         while (true) {
-            // If there are no more character groups in this node, it means we could not
+            // If there are no more PtNodes in this array, it means we could not
             // find a matching character for this depth, therefore there is no match.
-            if (0 >= charGroupCount) return NOT_A_VALID_WORD_POS;
-            const int charGroupPos = pos;
+            if (0 >= ptNodeCount) return NOT_A_VALID_WORD_POS;
+            const int ptNodePos = pos;
             const PatriciaTrieReadingUtils::NodeFlags flags =
                     PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
             int character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(mDictRoot,
                     &pos);
             if (character == wChar) {
-                // This is the correct node. Only one character group may start with the same
-                // char within a node, so either we found our match in this node, or there is
+                // This is the correct PtNode. Only one PtNode may start with the same char within
+                // a PtNode array, so either we found our match in this array, or there is
                 // no match and we can return NOT_A_VALID_WORD_POS. So we will check all the
-                // characters in this character group indeed does match.
+                // characters in this PtNode indeed does match.
                 if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) {
                     character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(mDictRoot,
                             &pos);
@@ -253,7 +253,7 @@
                         ++wordPos;
                         // If we shoot the length of the word we search for, or if we find a single
                         // character that does not match, as explained above, it means the word is
-                        // not in the dictionary (by virtue of this chargroup being the only one to
+                        // not in the dictionary (by virtue of this PtNode being the only one to
                         // match the word on the first character, but not matching the whole word).
                         if (wordPos >= length) return NOT_A_VALID_WORD_POS;
                         if (inWord[wordPos] != character) return NOT_A_VALID_WORD_POS;
@@ -268,7 +268,7 @@
                 ++wordPos;
                 if (PatriciaTrieReadingUtils::isTerminal(flags)) {
                     if (wordPos == length) {
-                        return charGroupPos;
+                        return ptNodePos;
                     }
                     PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
                 }
@@ -282,7 +282,7 @@
                         flags, &pos);
                 break;
             } else {
-                // This chargroup does not match, so skip the remaining part and go to the next.
+                // This PtNode does not match, so skip the remaining part and go to the next.
                 if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) {
                     PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH,
                             &pos);
@@ -301,7 +301,7 @@
                     mBigramListPolicy.skipAllBigrams(&pos);
                 }
             }
-            --charGroupCount;
+            --ptNodeCount;
         }
     }
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp
index 003b943..576a158 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp
@@ -23,15 +23,15 @@
 
 typedef PatriciaTrieReadingUtils PtReadingUtils;
 
-const PtReadingUtils::NodeFlags PtReadingUtils::MASK_GROUP_ADDRESS_TYPE = 0xC0;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0;
+const PtReadingUtils::NodeFlags PtReadingUtils::MASK_CHILDREN_POSITION_TYPE = 0xC0;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_NOPOSITION = 0x00;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_ONEBYTE = 0x40;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_TWOBYTES = 0x80;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_THREEBYTES = 0xC0;
 
 // Flag for single/multiple char group
 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_MULTIPLE_CHARS = 0x20;
-// Flag for terminal groups
+// Flag for terminal PtNodes
 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_TERMINAL = 0x10;
 // Flag for shortcut targets presence
 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_SHORTCUT_TARGETS = 0x08;
@@ -46,14 +46,14 @@
         const uint8_t *const buffer, const NodeFlags flags, int *const pos) {
     const int base = *pos;
     int offset = 0;
-    switch (MASK_GROUP_ADDRESS_TYPE & flags) {
-        case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
+    switch (MASK_CHILDREN_POSITION_TYPE & flags) {
+        case FLAG_CHILDREN_POSITION_TYPE_ONEBYTE:
             offset = ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
             break;
-        case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
+        case FLAG_CHILDREN_POSITION_TYPE_TWOBYTES:
             offset = ByteArrayUtils::readUint16AndAdvancePosition(buffer, pos);
             break;
-        case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
+        case FLAG_CHILDREN_POSITION_TYPE_THREEBYTES:
             offset = ByteArrayUtils::readUint24AndAdvancePosition(buffer, pos);
             break;
         default:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
index 9f2fc20..f76c387 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
@@ -28,7 +28,7 @@
  public:
     typedef uint8_t NodeFlags;
 
-    static AK_FORCE_INLINE int getGroupCountAndAdvancePosition(
+    static AK_FORCE_INLINE int getPtNodeArraySizeAndAdvancePosition(
             const uint8_t *const buffer, int *const pos) {
         const uint8_t firstByte = ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
         if (firstByte < 0x80) {
@@ -116,17 +116,17 @@
     }
 
     static AK_FORCE_INLINE bool hasChildrenInFlags(const NodeFlags flags) {
-        return FLAG_GROUP_ADDRESS_TYPE_NOADDRESS != (MASK_GROUP_ADDRESS_TYPE & flags);
+        return FLAG_CHILDREN_POSITION_TYPE_NOPOSITION != (MASK_CHILDREN_POSITION_TYPE & flags);
     }
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(PatriciaTrieReadingUtils);
 
-    static const NodeFlags MASK_GROUP_ADDRESS_TYPE;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_NOADDRESS;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
+    static const NodeFlags MASK_CHILDREN_POSITION_TYPE;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_NOPOSITION;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_ONEBYTE;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_TWOBYTES;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_THREEBYTES;
 
     static const NodeFlags FLAG_HAS_MULTIPLE_CHARS;
     static const NodeFlags FLAG_IS_TERMINAL;
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 6d4c05e..bb5b96a 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -25,7 +25,7 @@
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
@@ -239,17 +239,17 @@
 
         // check unigram
         for (final String word : words) {
-            final CharGroup cg = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
-            assertNotNull(cg);
+            final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+            assertNotNull(ptNode);
         }
 
         // check bigram
         for (int i = 0; i < bigrams.size(); ++i) {
             final int w1 = bigrams.keyAt(i);
             for (final int w2 : bigrams.valueAt(i)) {
-                final CharGroup cg = FusionDictionary.findWordInTree(dict.mRootNodeArray,
+                final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray,
                         words.get(w1));
-                assertNotNull(words.get(w1) + "," + words.get(w2), cg.getBigram(words.get(w2)));
+                assertNotNull(words.get(w1) + "," + words.get(w2), ptNode.getBigram(words.get(w2)));
             }
         }
 
@@ -257,11 +257,11 @@
         if (shortcutMap != null) {
             for (final Map.Entry<String, List<String>> entry : shortcutMap.entrySet()) {
                 assertTrue(words.contains(entry.getKey()));
-                final CharGroup group = FusionDictionary.findWordInTree(dict.mRootNodeArray,
+                final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray,
                         entry.getKey());
                 for (final String word : entry.getValue()) {
                     assertNotNull("shortcut not found: " + entry.getKey() + ", " + word,
-                            group.getShortcut(word));
+                            ptNode.getShortcut(word));
                 }
             }
         }
@@ -555,7 +555,7 @@
         int position = -1;
         try {
             final long now = System.nanoTime();
-            position = BinaryDictIOUtils.getTerminalPosition(dictDecoder, word);
+            position = dictDecoder.getTerminalPosition(word);
             diff = System.nanoTime() - now;
         } catch (IOException e) {
             Log.e(TAG, "IOException while getTerminalPosition", e);
@@ -596,16 +596,13 @@
         try {
             // too long word
             final String longWord = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(dictDecoder, longWord));
+            assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(longWord));
 
             // null
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(dictDecoder, null));
+            assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(null));
 
             // empty string
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(dictDecoder, ""));
+            assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(""));
         } catch (IOException e) {
         } catch (UnsupportedFormatException e) {
         }
@@ -655,16 +652,16 @@
 
         try {
             MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(dictDecoder, sWords.get(0)));
+                    dictDecoder.getTerminalPosition(sWords.get(0)));
             DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(0));
             assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(dictDecoder, sWords.get(0)));
+                    dictDecoder.getTerminalPosition(sWords.get(0)));
 
             MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(dictDecoder, sWords.get(5)));
+                    dictDecoder.getTerminalPosition(sWords.get(5)));
             DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(5));
             assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(dictDecoder, sWords.get(5)));
+                    dictDecoder.getTerminalPosition(sWords.get(5)));
         } catch (IOException e) {
         } catch (UnsupportedFormatException e) {
         }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
index 901cfdb..7a6708b 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
@@ -86,8 +86,8 @@
         return builder.toString();
     }
 
-    private static void printCharGroup(final CharGroupInfo info) {
-        Log.d(TAG, "    CharGroup at " + info.mOriginalAddress);
+    private static void printPtNode(final PtNodeInfo info) {
+        Log.d(TAG, "    PtNode at " + info.mOriginalAddress);
         Log.d(TAG, "        flags = " + info.mFlags);
         Log.d(TAG, "        parentAddress = " + info.mParentAddress);
         Log.d(TAG, "        characters = " + new String(info.mCharacters, 0,
@@ -115,12 +115,12 @@
             final FormatSpec.FormatOptions formatOptions) {
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
         Log.d(TAG, "Node at " + dictBuffer.position());
-        final int count = BinaryDictDecoderUtils.readCharGroupCount(dictBuffer);
-        Log.d(TAG, "    charGroupCount = " + count);
+        final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+        Log.d(TAG, "    ptNodeCount = " + count);
         for (int i = 0; i < count; ++i) {
-            final CharGroupInfo currentInfo = dictDecoder.readPtNode(dictBuffer.position(),
+            final PtNodeInfo currentInfo = dictDecoder.readPtNode(dictBuffer.position(),
                     formatOptions);
-            printCharGroup(currentInfo);
+            printPtNode(currentInfo);
         }
         if (formatOptions.mSupportsDynamicUpdate) {
             final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
@@ -142,8 +142,7 @@
 
         try {
             final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file);
-            dictDecoder.openDictBuffer();
-            position = BinaryDictIOUtils.getTerminalPosition(dictDecoder, word);
+            position = dictDecoder.getTerminalPosition(word);
         } catch (IOException e) {
         } catch (UnsupportedFormatException e) {
         }
@@ -155,13 +154,13 @@
      *
      * @param dictDecoder the dict decoder
      * @param word the word searched
-     * @return the found group
+     * @return the found ptNodeInfo
      * @throws IOException
      * @throws UnsupportedFormatException
      */
-    private static CharGroupInfo findWordByBinaryDictReader(final Ver3DictDecoder dictDecoder,
+    private static PtNodeInfo findWordByBinaryDictReader(final Ver3DictDecoder dictDecoder,
             final String word) throws IOException, UnsupportedFormatException {
-        int position = BinaryDictIOUtils.getTerminalPosition(dictDecoder, word);
+        int position = dictDecoder.getTerminalPosition(word);
         final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
         if (position != FormatSpec.NOT_VALID_WORD) {
             dictBuffer.position(0);
@@ -172,10 +171,10 @@
         return null;
     }
 
-    private CharGroupInfo findWordFromFile(final File file, final String word) {
-        CharGroupInfo info = null;
+    private PtNodeInfo findWordFromFile(final File file, final String word) {
+        final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file);
+        PtNodeInfo info = null;
         try {
-            final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file);
             dictDecoder.openDictBuffer();
             info = findWordByBinaryDictReader(dictDecoder, word);
         } catch (IOException e) {
@@ -328,7 +327,7 @@
         insertAndCheckWord(file, "banana", 0, false, null, null);
         insertAndCheckWord(file, "recursive", 60, true, banana, null);
 
-        final CharGroupInfo info = findWordFromFile(file, "recursive");
+        final PtNodeInfo info = findWordFromFile(file, "recursive");
         int bananaPos = getWordPosition(file, "banana");
         assertNotNull(info.mBigrams);
         assertEquals(info.mBigrams.size(), 1);
diff --git a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
index 8831df9..72b9478 100644
--- a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
@@ -25,7 +25,7 @@
 import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.Ver3DictDecoder;
 import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
 import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList;
@@ -89,12 +89,12 @@
 
     private void checkWordInFusionDict(final FusionDictionary dict, final String word,
             final ArrayList<String> expectedBigrams) {
-        final CharGroup group = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
-        assertNotNull(group);
-        assertTrue(group.isTerminal());
+        final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+        assertNotNull(ptNode);
+        assertTrue(ptNode.isTerminal());
 
         for (final String bigram : expectedBigrams) {
-            assertNotNull(group.getBigram(bigram));
+            assertNotNull(ptNode.getBigram(bigram));
         }
     }
 
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
index d790d06..66fd084 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
@@ -17,7 +17,7 @@
 package com.android.inputmethod.latin.dicttool;
 
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.makedict.Word;
 
@@ -121,7 +121,7 @@
     private static void diffWords(final FusionDictionary dict0, final FusionDictionary dict1) {
         boolean hasDifferences = false;
         for (final Word word0 : dict0) {
-            final CharGroup word1 = FusionDictionary.findWordInTree(dict1.mRootNodeArray,
+            final PtNode word1 = FusionDictionary.findWordInTree(dict1.mRootNodeArray,
                     word0.mWord);
             if (null == word1) {
                 // This word is not in dict1
@@ -151,7 +151,7 @@
             }
         }
         for (final Word word1 : dict1) {
-            final CharGroup word0 = FusionDictionary.findWordInTree(dict0.mRootNodeArray,
+            final PtNode word0 = FusionDictionary.findWordInTree(dict0.mRootNodeArray,
                     word1.mWord);
             if (null == word0) {
                 // This word is not in dict0
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
index fa8c5f7..350f427 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
@@ -18,7 +18,7 @@
 
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.makedict.Word;
 
@@ -65,20 +65,20 @@
 
     private static void showWordInfo(final FusionDictionary dict, final String word,
             final boolean plumbing) {
-        final CharGroup group = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
-        if (null == group) {
+        final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+        if (null == ptNode) {
             System.out.println(word + " is not in the dictionary");
             return;
         }
         System.out.println("Word: " + word);
-        System.out.println("  Freq: " + group.getFrequency());
-        if (group.getIsNotAWord()) {
+        System.out.println("  Freq: " + ptNode.getFrequency());
+        if (ptNode.getIsNotAWord()) {
             System.out.println("  Is not a word");
         }
-        if (group.getIsBlacklistEntry()) {
+        if (ptNode.getIsBlacklistEntry()) {
             System.out.println("  Is a blacklist entry");
         }
-        final ArrayList<WeightedString> shortcutTargets = group.getShortcutTargets();
+        final ArrayList<WeightedString> shortcutTargets = ptNode.getShortcutTargets();
         if (null == shortcutTargets || shortcutTargets.isEmpty()) {
             System.out.println("  No shortcuts");
         } else {
@@ -88,7 +88,7 @@
                                 ? "whitelist" : shortcutTarget.mFrequency) + ")");
             }
         }
-        final ArrayList<WeightedString> bigrams = group.getBigrams();
+        final ArrayList<WeightedString> bigrams = ptNode.getBigrams();
         if (null == bigrams || bigrams.isEmpty()) {
             System.out.println("  No bigrams");
         } else {
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java
index 22c0ceb..659650a 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java
@@ -17,7 +17,7 @@
 package com.android.inputmethod.latin.makedict;
 
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.Word;
@@ -72,8 +72,8 @@
         assertNotNull(dict);
         for (final String word : words) {
             if (--limit < 0) return;
-            final CharGroup cg = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
-            assertNotNull(cg);
+            final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+            assertNotNull(ptNode);
         }
     }