Repository: PyThaiNLP/nlpo3 (language: python)

Issue:
move cargo and build-related file inside rust_modules entirely
Move python wrapper into rust_modules, along with cargo file

might have to fix CI, run cargo test at rust_modules  instead of at root


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 8b2cfb0..2c26c9a 100644
--- a/README.md
+++ b/README.md
@@ -1,1 +1,77 @@
-# oxidized-thainlp
\ No newline at end of file
+
+# BETA Release
+
+```bash
+pip install pythainlp-rust-modules
+```
+
+
+# Feature
+
+- newmm word tokenization with default dict, ultra fast speed.
+- support custom dict.
+------------------------------------------------------------------------------------------------------------------
+
+------------------------
+# How To Build It Yourself.
+
+## Requirements
+
+- Rust 2018 Edition [Download here](https://www.rust-lang.org/tools/install)
+- Python 3.6+
+- PyO3 - already included in Cargo.toml
+- *** For Linux *** sudo apt install python3-dev python-dev
+- [Maturin](https://github.com/PyO3/maturin)
+
+
+
+At oxidized-thainlp/rust_modules directory, run the following commands.
+
+## Linux
+
+
+```bash
+
+
+maturin build --release -i python --manylinux off  
+# Or 
+maturin build --release -i python
+
+```
+
+## Windows Powershell
+```shell
+path\\to\\maturin.exe build --release -i python
+
+```
+
+## MacOS
+```zsh
+maturin build --release -i python
+```
+
+Note: You can try omitting "-i python". This will let Maturin build this lib for many versions of python if detected.
+
+This should generate a wheel file which can be installed by pip
+
+-------------------
+# How To Use In Python
+
+```python
+from oxidized_thainlp import segment,load_dict
+
+load_dict("path/to/dict.file","dict_name")
+segment("สวัสดีครับ","dict_name")
+
+
+
+```
+
+Just that!
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------
+# TODO
+
+- Distribute module on pip or whatever, I really do not know much about Python env.
+
+# oxidized-thainlp
diff --git a/RUST_MODULES.md b/RUST_MODULES.md
deleted file mode 100644
index 9626c5c..0000000
--- a/RUST_MODULES.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Feature
-
-- newmm word tokenization with default dict, ultra fast speed.
-- support custom dict.
-------------------------------------------------------------------------------------------------------------------
-# Requirements
-
-- Rust 2018 Edition [Download here](https://www.rust-lang.org/tools/install)
-- Python 3.6+
-- PyO3 - already included in Cargo.toml
-- *** For Linux *** sudo apt install python3-dev python-dev
-- [Maturin](https://github.com/PyO3/maturin)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-# How To Build 
-
-Linux
-
-At pythainlp root directory
-
-```bash
-
-maturin build --release -i python --manylinux off 
-
-```
-
-Windows Powershell
-```shell
-path\\to\\maturin.exe build --release -i python
-
-```
-
-This should generate a wheel file which can be installed by pip
-
--------------------
-# How To Use Built module
-
-```python
-from oxidized_thainlp import segment,load_dict
-
-
-```
-
-Just that!
-
---------------------------------------------------------------------------------------------------------------------------------------------------------
-# TODO
-
-- Distribute module on pip or whatever, I really do not know much about Python env.
\ No newline at end of file
diff --git a/notebooks/oxidized_segment_benchmarks.ipynb b/notebooks/oxidized_segment_benchmarks.ipynb
new file mode 100644
index 0000000..0a5c538
--- /dev/null
+++ b/notebooks/oxidized_segment_benchmarks.ipynb
@@ -0,0 +1,632 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "6PCaAU1y66FE"
+   },
+   "source": [
+    "# Benchmark `oxidized_thainlp.segment`"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This notebook benchmarks `oxidized_thainlp.segment` against `newmm` (unsafe) using the same dictionary."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {
+    "id": "iAlScT9d66FX",
+    "outputId": "fe7a569c-f384-4e95-cbc3-3d412a99fc74"
+   },
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "Reusing dataset wisesight_sentiment (/home/cstorm125/.cache/huggingface/datasets/wisesight_sentiment/wisesight_sentiment/1.0.0/4bb1772cff1a0703d72fb9e84dff9348e80f6cdf80b0f6c0f59bcd85fc5a3537)\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "DatasetDict({\n",
+       "    train: Dataset({\n",
+       "        features: ['texts', 'category'],\n",
+       "        num_rows: 21628\n",
+       "    })\n",
+       "    validation: Dataset({\n",
+       "        features: ['texts', 'category'],\n",
+       "        num_rows: 2404\n",
+       "    })\n",
+       "    test: Dataset({\n",
+       "        features: ['texts', 'category'],\n",
+       "        num_rows: 2671\n",
+       "    })\n",
+       "})"
+      ]
+     },
+     "execution_count": 1,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "from oxidized_thainlp import segment, load_dict\n",
+    "from pythainlp.tokenize import word_tokenize\n",
+    "from tqdm.auto import tqdm\n",
+    "import time\n",
+    "import numpy as np\n",
+    "import matplotlib.pyplot as plt\n",
+    "from datasets import load_dataset\n",
+    "\n",
+    "def time_func(func,arg):\n",
+    "    start_time = time.time()\n",
+    "    func(arg)\n",
+    "    return time.time() - start_time\n",
+    "\n",
+    "datasets = load_dataset('wisesight_sentiment')\n",
+    "datasets"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "Ssp84MKA66Fb"
+   },
+   "source": [
+    "## Load Custom Dictionary"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {
+    "id": "GCtUUACp66Fc",
+    "outputId": "fe1c7236-28b3-4ee4-a0e4-81312e7762fb"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'ไปจองมาแล้วนาจา Mitsubishi Attrage ได้หลังสงกรานต์เลย รอขับอยู่นาจา กระทัดรัด เหมาะกับสาวๆขับรถคนเดียวแบบเรา ราคาสบายกระเป๋า ประหยัดน้ำมัน วิ่งไกลแค่ไหนหายห่วงค่ะ'"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "txt = datasets['train']['texts'][0]\n",
+    "txt"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {
+    "id": "XHh2LIdG66Fd",
+    "outputId": "f1f8c12d-fd61-40f2-d31f-98c99cd120ae"
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Successful: dictionary name test from file ../words_th.txt has been successfully loaded\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "['ไป', 'จอง', 'มา', 'แล้', 'วนา', 'จา', ' ', 'Mitsubishi', ' ', 'Attrage']"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "#load as \"test\"\n",
+    "load_dict('../words_th.txt', 'test')\n",
+    "segment(txt, dict_name='test',safe=False)[:10]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {
+    "id": "X5fs073g66Fe"
+   },
+   "outputs": [],
+   "source": [
+    "def oxidized_newmm(txt,safe=False,parallel=False):\n",
+    "    return segment(txt, dict_name='test',safe=safe,parallel=parallel)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "id": "vNhyGLz766Fg",
+    "outputId": "47c231d7-cfa5-462f-c64b-06a99d40996c"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['ไป', 'จอง', 'มา', 'แล้', 'วนา', 'จา', ' ', 'Mitsubishi', ' ', 'Attrage']"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "from pythainlp.tokenize import dict_trie\n",
+    "with open('../words_th.txt','r') as f:\n",
+    "    words_th = f.readlines()\n",
+    "    words_th = [i.strip() for i in words_th]\n",
+    "trie = dict_trie(dict_source=words_th)\n",
+    "word_tokenize(txt, engine=\"newmm\", custom_dict=trie)[:10]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "id": "FSFVxKh166Fg"
+   },
+   "outputs": [],
+   "source": [
+    "def old_newmm(txt,engine='newmm'):\n",
+    "    return word_tokenize(txt, engine=engine, custom_dict=trie)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "Qyrh-uny66Fh"
+   },
+   "source": [
+    "## One Example"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {
+    "id": "6xiWTHxo66Fi",
+    "outputId": "f6c29279-c121-48d4-8211-fd3f80b89f39"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'ไปจองมาแล้วนาจา Mitsubishi Attrage ได้หลังสงกรานต์เลย รอขับอยู่นาจา กระทัดรัด เหมาะกับสาวๆขับรถคนเดียวแบบเรา ราคาสบายกระเป๋า ประหยัดน้ำมัน วิ่งไกลแค่ไหนหายห่วงค่ะ'"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "txt = datasets['train']['texts'][0]\n",
+    "txt"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "9YqrA2Pb66Fj"
+   },
+   "source": [
+    "### Average Run Time for One Example"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "id": "Iz58c6Ff66Fj",
+    "outputId": "0d45dec2-bece-494e-8725-31ffdc97e1de"
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "413 µs ± 116 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "%timeit old_newmm(txt)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "id": "8jvijfPJ66Fl",
+    "outputId": "64a89f5b-468e-4af5-da35-6c938019e021"
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "176 µs ± 29 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "%timeit oxidized_newmm(txt)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "id": "lAdLgqdu66Fm",
+    "outputId": "42e39b71-9331-4311-a401-61fa68c21fde"
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "305 µs ± 16.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "%timeit oxidized_newmm(txt,parallel=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "L0lWERZk66Fm"
+   },
+   "source": [
+    "## All Examples in `wisesight_sentiment`"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "GXmhzISZ66Fn"
+   },
+   "source": [
+    "### Check If Results Match for All Examples"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {
+    "id": "ZY9Mosag66Fn",
+    "outputId": "46a56ce0-fd20-430a-d9b6-9564f4c25141"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(1.0, 21628)"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "corrects = [oxidized_newmm(txt)==old_newmm(txt) for txt in datasets['train']['texts']]\n",
+    "np.mean(corrects), len(corrects)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "Tyc_cHaf66Fo"
+   },
+   "source": [
+    "### Average Run Time Across All Examples"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {
+    "id": "uYeUydsQ66Fo",
+    "outputId": "b20ed761-fa2b-42b8-8a6c-ff3a1d2dc6b3"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(0.00023279344173506887, 8.558928249987741e-05, 2.7198900952978815)"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "old_newmms = [time_func(old_newmm, txt) for txt in datasets['train']['texts']]\n",
+    "oxidized_newmms = [time_func(oxidized_newmm, txt) for txt in datasets['train']['texts']]\n",
+    "\n",
+    "#old newmm runtime is about 2 times larger on average\n",
+    "np.mean(old_newmms), np.mean(oxidized_newmms), np.mean(old_newmms)/np.mean(oxidized_newmms)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {
+    "id": "8hRoDxm966Fp",
+    "outputId": "c4e8c0fd-97ca-4e3a-ee63-1281f84bb1d9"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(array([1.8693e+04, 1.9220e+03, 5.4600e+02, 1.9100e+02, 1.0700e+02,\n",
+       "        6.1000e+01, 3.6000e+01, 1.8000e+01, 1.8000e+01, 1.3000e+01,\n",
+       "        1.4000e+01, 3.0000e+00, 1.0000e+00, 0.0000e+00, 2.0000e+00,\n",
+       "        0.0000e+00, 0.0000e+00, 1.0000e+00, 0.0000e+00, 0.0000e+00,\n",
+       "        1.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00,\n",
+       "        0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, 1.0000e+00]),\n",
+       " array([5.24520874e-06, 1.58222516e-04, 3.11199824e-04, 4.64177132e-04,\n",
+       "        6.17154439e-04, 7.70131747e-04, 9.23109055e-04, 1.07608636e-03,\n",
+       "        1.22906367e-03, 1.38204098e-03, 1.53501829e-03, 1.68799559e-03,\n",
+       "        1.84097290e-03, 1.99395021e-03, 2.14692752e-03, 2.29990482e-03,\n",
+       "        2.45288213e-03, 2.60585944e-03, 2.75883675e-03, 2.91181405e-03,\n",
+       "        3.06479136e-03, 3.21776867e-03, 3.37074598e-03, 3.52372328e-03,\n",
+       "        3.67670059e-03, 3.82967790e-03, 3.98265521e-03, 4.13563251e-03,\n",
+       "        4.28860982e-03, 4.44158713e-03, 4.59456444e-03]),\n",
+       " <a list of 30 Patch objects>)"
+      ]
+     },
+     "execution_count": 14,
+     "metadata": {},
+     "output_type": "execute_result"
+    },
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "#look at distribution; oxidized newmm also consistently performs better\n",
+    "plt.hist(old_newmms,bins=30,alpha=0.5)\n",
+    "plt.hist(oxidized_newmms,bins=30,alpha=0.5)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "id": "EMZZ8SgY66Fp"
+   },
+   "source": [
+    "## Run Time as Sequence Length Grows"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {
+    "id": "3P_z59rS66Fp",
+    "outputId": "5951dd75-388c-4f9e-a1df-f0e98f663ec3"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "('เปิดศักราชใหม่! นายกฯ แถลงข่าวก่อนการแข่งขัน', 10)"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "txt = datasets['train']['texts'][1]\n",
+    "txt2 = ''.join(oxidized_newmm(txt)[:10])\n",
+    "txt2,len(oxidized_newmm(txt2))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {
+    "colab": {
+     "referenced_widgets": [
+      "0689a5cf946049a0ac98bdf9e1353810",
+      "2b481450056f4c1883c163bf066110a3"
+     ]
+    },
+    "id": "FEmDkPHL66Fq",
+    "outputId": "2b2c96ab-7044-423b-9b40-e06ca186a213"
+   },
+   "outputs": [
+    {
+     "data": {
+      "application/vnd.jupyter.widget-view+json": {
+       "model_id": "8b13abe39ef7472bb835b6a531a4cf82",
+       "version_major": 2,
+       "version_minor": 0
+      },
+      "text/plain": [
+       "HBox(children=(FloatProgress(value=0.0, max=5.0), HTML(value='')))"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n"
+     ]
+    },
+    {
+     "data": {
+      "application/vnd.jupyter.widget-view+json": {
+       "model_id": "59d39283c1ec44bfa63bd223fb2e0d2d",
+       "version_major": 2,
+       "version_minor": 0
+      },
+      "text/plain": [
+       "HBox(children=(FloatProgress(value=0.0, max=5.0), HTML(value='')))"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n"
+     ]
+    }
+   ],
+   "source": [
+    "old_newmms = [time_func(old_newmm, txt2*i) for i in tqdm([10**j for j in range(5)])]\n",
+    "oxidized_newmms = [time_func(oxidized_newmm, txt2*i) for i in tqdm([10**j for j in range(5)])]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "metadata": {
+    "id": "sT8GL0oX66Fr",
+    "outputId": "6bb4acfd-4721-47cb-d8ff-943c67a4cedf"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[<matplotlib.lines.Line2D at 0x7fc71321f2b0>]"
+      ]
+     },
+     "execution_count": 26,
+     "metadata": {},
+     "output_type": "execute_result"
+    },
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "#performance starts really deviate when sequence length > 10^3 tokens and above\n",
+    "positions = [i for i in range(5)]\n",
+    "labels = [f'10^{i+1}' for i in range(5)]\n",
+    "plt.xticks(positions, labels)\n",
+    "plt.plot(old_newmms,linestyle='dashed')\n",
+    "plt.plot(oxidized_newmms)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "metadata": {
+    "id": "5YU5aiNs66Fs"
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[<matplotlib.lines.Line2D at 0x7fc716a56320>]"
+      ]
+     },
+     "execution_count": 27,
+     "metadata": {},
+     "output_type": "execute_result"
+    },
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "#zooming in on inflexion point\n",
+    "positions = [i for i in range(4)]\n",
+    "labels = [f'10^{i+1}' for i in range(4)]\n",
+    "plt.xticks(positions, labels)\n",
+    "plt.plot(old_newmms[:-1],linestyle='dashed')\n",
+    "plt.plot(oxidized_newmms[:-1])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "colab": {
+   "name": "oxidized_segment.ipynb",
+   "provenance": []
+  },
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.6.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/oxidized_thainlp/__init__.py b/oxidized_thainlp2/__init__.py
similarity index 100%
rename from oxidized_thainlp/__init__.py
rename to oxidized_thainlp2/__init__.py
diff --git a/oxidized_thainlp2/__pycache__/__init__.cpython-36.pyc b/oxidized_thainlp2/__pycache__/__init__.cpython-36.pyc
new file mode 100644
index 0000000..b60525f
Binary files /dev/null and b/oxidized_thainlp2/__pycache__/__init__.cpython-36.pyc differ
diff --git a/pyproject.toml b/pyproject.toml
index 28234d6..9a2f56d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,3 @@
 [build-system]
 requires = ["maturin>=0.10,<0.11"]
 build-backend = "maturin"
-
-[tool.maturin]
-manylinux = "off"
\ No newline at end of file
diff --git a/rust_modules/Cargo.toml b/rust_modules/Cargo.toml
new file mode 100644
index 0000000..c36d1c1
--- /dev/null
+++ b/rust_modules/Cargo.toml
@@ -0,0 +1,42 @@
+[package]
+name = "pythainlp-rust-modules"
+version = "0.2.0"
+edition = "2018"
+include = ["pyproject.toml"]
+authors = ["Thanathip Suntorntip Gorlph"]
+
+[lib]
+name = "oxidized_thainlp"
+path = "src/lib.rs"
+# "cdylib" is necessary to produce a shared library for Python to import from.
+#
+# Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
+# to `use string_sum;` unless the "rlib" or "lib" crate type is also included, e.g.:
+# crate-type = ["cdylib", "rlib"]
+crate-type = ["cdylib", "rlib"]
+
+
+[dependencies.pyo3]
+version = "0.13.2"
+features = ["extension-module"]
+
+[dependencies]
+regex = "1.4.6"
+rayon = "1.5"
+binary-heap-plus = "0.4.1"
+lazy_static = "1.4.0"
+bytecount = "0.6.2"
+smol_str = "0.1.17"
+rustc-hash = "1.1.0"
+ahash = "0.7.2"
+
+
+[[test]]
+name = "basic" 
+path = "tests/test_tokenizer.rs"
+test = true
+
+[[test]]
+name = "basic2" 
+path = "tests/test_custom_string.rs"
+test = true
diff --git a/rust_modules/oxidized_thainlp/__init__.py b/rust_modules/oxidized_thainlp/__init__.py
new file mode 100644
index 0000000..80407e4
--- /dev/null
+++ b/rust_modules/oxidized_thainlp/__init__.py
@@ -0,0 +1,48 @@
+from .oxidized_thainlp import (
+    load_dict as load_dict_to_oxidized,
+    segment as rust_segment,
+)
+import codecs
+from typing import List
+
+
+def load_dict(file_path: str, dict_name: str):
+    """Load dictionary from a file.
+
+    :param file_path: Absolute path to a dictionary file
+    :type file_path: str
+    :param dict_name: A unique dictionary name, use for reference.
+        Can be any valid utf-8 string, except "default" which is reserved.
+    :type dict_name: str
+    """
+    load_result = load_dict_to_oxidized(file_path, dict_name)
+    print(load_result)
+
+
+def segment(
+    text: str,
+    dict_name: str = "default",
+    safe: bool = False,
+    parallel: bool = False,
+) -> List[str]:
+    """Break text into tokens.
+
+    This method is an implementation of newmm segmentaion.
+    Support multithread mode - set by parallel flag.
+
+    :param text: Input text
+    :type text: str
+    :param dict_name: Path to dictionary, defaults to "default"
+    :type dict_name: str, optional
+    :param safe: Use safe mode to avoid extra long waiting time in long text,
+        defaults to False
+    :type safe: bool, optional
+    :param parallel: Use multithread mode, defaults to False
+    :type parallel: bool, optional
+    :return: List of tokens
+    :rtype: List[str]
+    """
+    result = rust_segment(text, dict_name, safe, parallel)
+    for (index, utf8bytes) in enumerate(result):
+        result[index] = codecs.decode(bytearray(utf8bytes), encoding="utf-8")
+    return result
diff --git a/rust_modules/pyproject.toml b/rust_modules/pyproject.toml
new file mode 100644
index 0000000..aacb0ed
--- /dev/null
+++ b/rust_modules/pyproject.toml
@@ -0,0 +1,4 @@
+[build-system]
+requires = ["maturin>=0.10,<0.11"]
+build-backend = "maturin"
+
diff --git a/rust_modules/src/NOTE_ON_STRING.md b/rust_modules/src/NOTE_ON_STRING.md
index 9073a17..1b4b588 100644
--- a/rust_modules/src/NOTE_ON_STRING.md
+++ b/rust_modules/src/NOTE_ON_STRING.md
@@ -14,6 +14,3 @@ For "space" in regex, it is padded with \x00\x00\x00.
 
 
 [More on UTF-8](https://en.wikipedia.org/wiki/UTF-8)
-
-
-*** ALL Vec<u8> or &[u8] operated in tcc_custom, trie_custom and newmm_custom 
\ No newline at end of file
diff --git a/rust_modules/src/fixed_bytes_str/four_bytes.rs b/rust_modules/src/fixed_bytes_str/four_bytes.rs
index cdd7a1c..5a4c5a2 100644
--- a/rust_modules/src/fixed_bytes_str/four_bytes.rs
+++ b/rust_modules/src/fixed_bytes_str/four_bytes.rs
@@ -18,7 +18,6 @@ use lazy_static::__Deref;
 use smol_str::SmolStr;
 use std::{borrow::Borrow, str::from_utf8_unchecked};
 
-
 pub type ValidUTF8BytesVec = Vec<u8>;
 pub type CustomStringBytesVec = Vec<u8>;
 pub type ValidUTF8BytesSlice = [u8];
@@ -27,7 +26,7 @@ fn is_in_range<T: PartialEq + PartialOrd>(value: T, range: (T, T)) -> bool {
     value >= range.0 && value <= range.1
 }
 /** returns bytes index */
-pub fn rfind_space(custom_text: &[u8]) -> Option<usize> {
+pub fn rfind_space(custom_text: &CustomStringBytesSlice) -> Option<usize> {
     assert_eq!(custom_text.len() % 4, 0);
 
     for index in (0..(custom_text.len() / BYTES_PER_CHAR)).rev() {
@@ -41,7 +40,7 @@ pub fn rfind_space(custom_text: &[u8]) -> Option<usize> {
     None
 }
 /** return char index */
-pub fn rfind_space_char_index(custom_text: &[u8]) -> Option<usize> {
+pub fn rfind_space_char_index(custom_text: &CustomStringBytesSlice) -> Option<usize> {
     assert_eq!(custom_text.len() % 4, 0);
 
     for index in (0..(custom_text.len() / BYTES_PER_CHAR)).rev() {
@@ -61,7 +60,7 @@ pub fn rfind_space_char_index(custom_text: &[u8]) -> Option<usize> {
  28 ..
 
 */
-fn is_whitespace(custom_bytes: &[u8]) -> bool {
+fn is_whitespace(custom_bytes: &CustomStringBytesSlice) -> bool {
     match custom_bytes {
         [0, 0, 0, 9]
         | [0, 0, 0, 10]
@@ -77,7 +76,7 @@ fn is_whitespace(custom_bytes: &[u8]) -> bool {
         _ => false,
     }
 }
-pub fn to_four_bytes(input: &str) -> Vec<u8> {
+pub fn to_four_bytes(input: &str) -> CustomStringBytesVec {
     let output_size = num_chars(input.as_bytes());
     let mut output_vec: Vec<u8> = Vec::with_capacity(output_size * BYTES_PER_CHAR);
     // let mut output:&[u8;4] = &[0;output_size];
@@ -99,7 +98,7 @@ pub fn to_four_bytes(input: &str) -> Vec<u8> {
     output_vec.shrink_to_fit();
     output_vec
 }
-pub fn encode_utf8(input: &[u8]) -> SmolStr {
+pub fn encode_utf8(input: &CustomStringBytesSlice) -> SmolStr {
     assert_eq!(input.len(), 4);
     match input {
         [0, 0, 0, one_byte_char] if one_byte_char <= &VALID_ONE_BYTE_UTF8_FIRST_BYTE_MAX_VALUE => {
@@ -131,7 +130,7 @@ pub fn encode_utf8(input: &[u8]) -> SmolStr {
         }
     }
 }
-pub fn trim_to_std_utf8(input: &[u8]) -> Box<[u8]> {
+pub fn trim_to_std_utf8(input: &CustomStringBytesSlice) -> Box<[u8]> {
     assert_eq!(input.len(), 4);
     match input {
         [0, 0, 0, one_byte_char] if one_byte_char <= &VALID_ONE_BYTE_UTF8_FIRST_BYTE_MAX_VALUE => {
@@ -163,9 +162,9 @@ pub fn trim_to_std_utf8(input: &[u8]) -> Box<[u8]> {
         }
     }
 }
-pub fn to_std_string(input: &[u8]) -> String {
+pub fn to_std_string(input: &CustomStringBytesSlice) -> String {
     assert_eq!(input.len() % 4, 0);
-    let mut output_content: Vec<u8> = Vec::with_capacity(input.len()/10);
+    let mut output_content: Vec<u8> = Vec::with_capacity(input.len() / 10);
     for index in (0..input.len()).step_by(BYTES_PER_CHAR) {
         let end_offset = index + BYTES_PER_CHAR;
         for byte in trim_to_std_utf8(&input[index..end_offset]).deref() {
@@ -195,21 +194,18 @@ pub struct CustomString {
 }
 
 impl CustomString {
-    pub fn from(four_byte_vec: Vec<u8>) -> Self {
-        let content =  four_byte_vec;
+    pub fn from(four_byte_vec: ValidUTF8BytesVec) -> Self {
+        let content = four_byte_vec;
         let length = content.len() / BYTES_PER_CHAR;
-        Self {
-            content,
-            length
-        }
+        Self { content, length }
     }
     pub fn new(base_string: &str) -> Self {
         let content = Vec::from(to_four_bytes(base_string));
         let length = content.len() / BYTES_PER_CHAR;
         Self { content, length }
     }
-    pub fn substring_as_custom_bytes(&self,char_start:usize,char_end:usize)->&[u8]{
-        &self.content[(char_start*BYTES_PER_CHAR)..(char_end*BYTES_PER_CHAR)]
+    pub fn substring_as_custom_bytes(&self, char_start: usize, char_end: usize) -> &[u8] {
+        &self.content[(char_start * BYTES_PER_CHAR)..(char_end * BYTES_PER_CHAR)]
     }
     pub fn raw_content(&self) -> &[u8] {
         self.content.borrow()
@@ -242,8 +238,9 @@ impl CustomString {
         }
     }
     /** Modify this object - remove "chars" from start to end index */
-    pub fn remove_by_chars_indices(&mut self,start:usize,end:usize){
-        self.content.drain((start*BYTES_PER_CHAR)..(end*BYTES_PER_CHAR));
+    pub fn remove_by_chars_indices(&mut self, start: usize, end: usize) {
+        self.content
+            .drain((start * BYTES_PER_CHAR)..(end * BYTES_PER_CHAR));
         self.length = self.content.len() / 4;
     }
 
@@ -256,7 +253,7 @@ impl CustomString {
         to_std_string(&self.content)
     }
     pub fn convert_raw_bytes_to_std_string(input: &[u8]) -> String {
-        let mut output_content: Vec<u8> = Vec::with_capacity(input.len()/100);
+        let mut output_content: Vec<u8> = Vec::with_capacity(input.len() / 100);
         for index in (0..input.len()).step_by(BYTES_PER_CHAR) {
             let end_offset = index + BYTES_PER_CHAR;
             for byte in trim_to_std_utf8(&input[index..end_offset]).deref() {
@@ -268,7 +265,7 @@ impl CustomString {
         output
     }
     pub fn convert_raw_bytes_to_utf8_bytes(input: &[u8]) -> Vec<u8> {
-        let mut output_content: Vec<u8> = Vec::with_capacity(input.len()/100);
+        let mut output_content: Vec<u8> = Vec::with_capacity(input.len() / 100);
         for index in (0..input.len()).step_by(BYTES_PER_CHAR) {
             let end_offset = index + BYTES_PER_CHAR;
             for byte in trim_to_std_utf8(&input[index..end_offset]).deref() {
@@ -279,49 +276,46 @@ impl CustomString {
         output_content.to_vec()
     }
 }
-// impl PartialEq for CustomString {
 
 pub trait FixedCharsLengthByteSlice {
     fn slice_by_char_indice(&self, start: usize, end: usize) -> Self;
     fn chars_len(&self) -> usize;
-    fn is_valid_custom_str_bytes(&self)->bool;
+    fn is_valid_custom_str_bytes(&self) -> bool;
 }
-impl FixedCharsLengthByteSlice for &[u8] {
+impl FixedCharsLengthByteSlice for &CustomStringBytesSlice {
     fn slice_by_char_indice(&self, start: usize, end: usize) -> Self {
         &self[(start * BYTES_PER_CHAR)..(end * BYTES_PER_CHAR)]
     }
     fn chars_len(&self) -> usize {
         self.len() / BYTES_PER_CHAR
     }
-    fn is_valid_custom_str_bytes(&self) ->bool {
+    fn is_valid_custom_str_bytes(&self) -> bool {
         if self.len() % 4 != 0 {
-            return false; 
-        }  
+            return false;
+        }
         for index in 0..self.chars_len() {
-            let current_slice = self.slice_by_char_indice(index, index+1);
+            let current_slice = self.slice_by_char_indice(index, index + 1);
             match current_slice {
-                [0, 0, 0, one_byte_char] if one_byte_char <= &VALID_ONE_BYTE_UTF8_FIRST_BYTE_MAX_VALUE => {},
+                [0, 0, 0, one_byte_char]
+                    if one_byte_char <= &VALID_ONE_BYTE_UTF8_FIRST_BYTE_MAX_VALUE => {}
                 [0, 0, first_byte, second_byte]
                     if is_in_range(*first_byte, VALID_TWO_BYTE_UTF8_FIRST_BYTE_RANGE)
-                        && is_in_range(*second_byte, VALID_TWO_BYTE_UTF8_SECOND_BYTE_RANGE)  => {},
+                        && is_in_range(*second_byte, VALID_TWO_BYTE_UTF8_SECOND_BYTE_RANGE) => {}
                 [0, first_byte, second_byte, third_byte]
                     if is_in_range(*first_byte, VALID_THREE_BYTE_UTF8_FIRST_BYTE_RANGE)
                         && is_in_range(*second_byte, VALID_THREE_BYTE_UTF8_SECOND_BYTE_RANGE)
-                        && is_in_range(*third_byte, VALID_THREE_BYTE_UTF8_THIRD_BYTE_RANGE)  => {},
+                        && is_in_range(*third_byte, VALID_THREE_BYTE_UTF8_THIRD_BYTE_RANGE) => {}
                 [first_byte, second_byte, third_byte, fourth_byte]
                     if is_in_range(*first_byte, VALID_FOUR_BYTE_UTF8_FIRST_BYTE_RANGE)
                         && is_in_range(*second_byte, VALID_FOUR_BYTE_UTF8_SECOND_BYTE_RANGE)
                         && is_in_range(*third_byte, VALID_FOUR_BYTE_UTF8_THIRD_BYTE_RANGE)
-                        && is_in_range(*fourth_byte, VALID_FOUR_BYTE_UTF8_FOURTH_BYTE_RANGE)   => {}
-                            
-                        
+                        && is_in_range(*fourth_byte, VALID_FOUR_BYTE_UTF8_FOURTH_BYTE_RANGE) => {}
+
                 _ => {
                     return false;
                 }
             }
-
         }
         true
-     
     }
 }
diff --git a/rust_modules/src/main.rs b/rust_modules/src/main.rs
deleted file mode 100644
index dfdb486..0000000
--- a/rust_modules/src/main.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-use oxidized_thainlp::{fixed_bytes_str::four_bytes::CustomString};
-use oxidized_thainlp::tokenizer::{newmm_custom::{Newmm as NewmmCustom},trie_custom,dict_reader_custom::{DictSource as CustomDictSource,create_dict_trie as create_custom_dict_trie}};
-use regex::internal::Inst;
-use std::path::{Path,PathBuf};
-use std::env;
-use std::time::Instant;
-const DEFAULT_DICT_PATH_RELATIVE_TO_ROOT:&str = "./pythainlp/corpus/words_th.txt"; 
-use oxidized_thainlp::fixed_bytes_str::four_bytes;
-
-const CARGO_ROOT:&str = env!("CARGO_MANIFEST_DIR");
-const short_text_1:&str ="หมอนทองตากลมหูว์MBK39 :.ฉฺ๐๐๓-#™±";
-const short_text_2:&str = "ทดสอบ";
-
-fn main(){
-  
-    let long_text = [ 
-        "ไต้หวัน (แป่ะเอ๋ยี้: Tâi-oân; ไต่อวัน) หรือ ไถวาน ",
-        "(อักษรโรมัน: Taiwan; จีนตัวย่อ: 台湾; จีนตัวเต็ม: 臺灣/台灣; พินอิน: ",
-        "Táiwān; ไถวาน) หรือชื่อทางการว่า สาธารณรัฐจีน (จีนตัวย่อ: 中华民国; ",
-        "จีนตัวเต็ม: 中華民國; พินอิน: Zhōnghuá ",
-        "Mínguó) เป็นรัฐในทวีปเอเชียตะวันออก[7][8][9] ปัจจุบันประกอบด้วย",
-        "เกาะใหญ่ 5 แห่ง คือ จินเหมิน (金門), ไต้หวัน, เผิงหู (澎湖), หมาจู่ ",
-        "(馬祖), และอูชิว (烏坵) กับทั้งเกาะเล็กเกาะน้อยอีกจำนวนหนึ่ง ",
-        "ท้องที่ดังกล่าวเรียกรวมกันว่า \"พื้นที่ไต้หวัน\" (臺灣地區)\n",
-        "ไต้หวันด้านตะวันตกติดกับจีนแผ่นดินใหญ่ ด้านตะวันออกและตะวันออก",
-        "เฉียงเหนือติดกับญี่ปุ่น และด้านใต้ติดกับฟิลิปปินส์ กรุงไทเปเป็น",
-        "เมืองหลวง ส่วนไทเปใหม่เป็นเขตปกครองที่จัดตั้งขึ้นใหม่ กินพื้นที่",
-        "กรุงไทเปและเป็นเขตซึ่งประชากรหนาแน่นที่สุดในเวลานี้\n",
-        "เกาะไต้หวันเดิมเป็นที่อยู่ของชนพื้นเมือง และมีชาวจีนจากแผ่นดิน",
-        "ใหญ่เข้ามาอาศัยร่วมด้วย จนกระทั่งชาววิลันดาและสเปนเดินทางเข้า",
-        "มาในยุคสำรวจเมื่อศตวรรษที่ 17 และมาตั้งบ้านเรือนกลายเป็นนิคม",
-        "ใหญ่โต ต่อมาปี 1662 ราชวงศ์หมิงในแผ่นดินใหญ่ถูกราชวงศ์ชิงแทนที่ ",
-        "เจิ้ง เฉิงกง (鄭成功) ขุนศึกหมิง รวมกำลังหนีมาถึงเกาะไต้หวัน ",
-        "และรุกไล่ฝรั่งออกไปได้อย่างราบคาบ เขาจึงตั้งราชอาณาจักรตงหนิง ",
-        "(東寧) ขึ้นบนเกาะเพื่อ \"โค่นชิงฟื้นหมิง\" แต่ในปี 1683 ราชวงศ์",
-        "ชิงปราบปรามอาณาจักรตงหนิงและเข้าครอบครองไต้หวันเป็นผลสำเร็จ ",
-        "ไต้หวันจึงกลายเป็นมณฑลหนึ่งของจีน อย่างไรก็ดี ความบาดหมางระหว่าง",
-        "จีนกับญี่ปุ่นเป็นเหตุให้ญี่ปุ่นได้ไต้หวันไปในปี 1895\n",
-        "ก่อนเสียไต้หวันคืนแก่จีนหลังสงครามโลกครั้งที่สอง ช่วงนั้น มีการ",
-        "เปลี่ยนแปลงการปกครองในจีน พรรคก๊กมินตั๋ง ได้เป็นใหญ่ ",
-        "แต่ไม่นานก็เสียทีให้แก่พรรคคอมมิวนิสต์จีน พรรคก๊กมินตั๋งจึงหนี",
-        "มายังเกาะไต้หวันและสถาปนาสาธารณรัฐจีนขึ้นบนเกาะแยกต่างหาก ",
-        "ส่วนฝ่ายคอมมิวนิสต์จีนที่เป็นฝ่ายได้รับชัยชนะได้สถาปนาสาธารณรัฐ",
-        "ประชาชนจีนบนแผ่นดินใหญ่ อย่างไรก็ดี จีนยังคงถือว่า ไต้หวันเป็น",
-        "มณฑลหนึ่งของตน และไต้หวันเองก็ยังมิได้รับการยอมรับจากนานาชาติ",
-        "ว่าเป็นประเทศเอกราชมาจนบัดนี้\n",
-        "ในช่วงทศวรรษ 1980 ถึงต้นทศวรรษ 1990 การเมืองการปกครอง",
-        "สาธารณรัฐจีน (ไต้หวัน) เจริญรุ่งเรืองจนเป็นประชาธิปไตยที่มีพรรค",
-        "การเมืองหลายพรรคและมีการเลือกตั้งทั่วหน้า ในช่วงกลางศตวรรษที่ ",
-        "20 เศรษฐกิจไต้หวันงอกงามอย่างรวดเร็ว ไต้หวันจึงกลายเป็นประเทศ",
-        "พัฒนาแล้ว ได้ชื่อว่าเป็นหนึ่งในสี่เสือแห่งเอเชีย มีอุตสาหกรรม",
-        "ล้ำหน้า และมีเศรษฐกิจใหญ่โตเป็นอันดับที่ 19 ของโลก[11][12] ",
-        "อุตสาหกรรมที่ใช้เทคโนโลยีชั้นสูงของไต้หวันยังมีบทบาทสำคัญมากใน",
-        "เศรษฐกิจโลก เป็นเหตุให้ไต้หวันได้เป็นสมาชิกองค์การการค้าโลกและ",
-        "ความร่วมมือทางเศรษฐกิจเอเชีย-แปซิฟิก เสรีภาพของสื่อมวลชน เสรี",
-        "ภาพทางเศรษฐกิจ การสาธารณสุข[13]การศึกษา และดัชนีการพัฒนามนุษย์ใน",
-        "ไต้หวันยังได้รับการจัดอยู่ในอันดับสูงด้วย[14][4][15]\n",
-        "สาธารณรัฐจีน มีลักษณะเป็นกลุ่มเกาะ ภูมิประเทศติดกับทะเล ไม่ติด",
-        "กับประเทศใดเลย ห่างจากเกาะทางทิศเหนือและทิศตะวันตกเป็นสาธารณรัฐ",
-        "ประชาชนจีน ทิศใต้เป็นประเทศฟิลิปปินส์และทะเลจีนใต้ ส่วนทิศ",
-        "ตะวันออกเป็นมหาสมุทรแปซิฟิก\n",
-        "ในปี ค.ศ. 1638 หลังการพ่ายแพ้ของหลานชายของเจิ้ง เฉิงกง ",
-        "จากการบุกโจมตีทางทัพเรือของราชวงศ์ชิงแมนจูที่นำทัพโดยชื่อ หลาง",
-        "จากทางใต้ของมณฑลฝูเจี้ยน ทำให้ราชวงศ์ชิงผนวกยึดเกาะไต้หวันเป็น",
-        "ส่วนหนึ่งสำเร็จ และวางไว้ภายใต้เขตอำนาจของมณฑลฝูเจี้ยน ราชสำนัก",
-        "ราชวงศ์ชิงพยายามลดการละเมิดสิทธิ์และความไม่ลงรอยกันในพื้นที่โดย",
-        "ออกกฎหมายเพื่อจัดการตรวจคนเข้าเมืองและเคารพสิทธิในที่ดินของชน",
-        "พื้นเมืองไต้หวัน ผู้อพยพจากฝูเจี้ยนทางใต้ส่วนใหญ่ยังคงเดินทางไป",
-        "ไต้หวัน เขตแดนระหว่างดินแดนที่เสียภาษีและสิ่งที่ถูกพิจารณาว่า",
-        "เป็นดินแดน \"เขตอันตราย\" เปลี่ยนไปทางทิศตะวันออกโดยชาวพื้นเมือง",
-        "บางคนเข้ารีตรับวัฒนธรรมแบบจีน ในขณะที่คนอื่นถอยกลับเข้าในภูเขา ",
-        "ในช่วงเวลานี้มีความขัดแย้งจำนวนมากระหว่างกลุ่มชาวฮั่นด้วยกันเอง",
-        "จากภูมิภาคต่าง ๆ ของฝูเจี้ยนทางใต้โดยเฉพาะอย่างยิ่งระหว่างเฉวียน",
-        "โจวกับฉางโจว และระหว่างฝูเจี้ยนตอนใต้และชาวพื้นเมืองไต้หวัน\n",
-        "พ.ศ. 2454 (ค.ศ. 1911) การจลาจลอู่ฮั่นในประเทศจีน เป็นจุดเริ่มต้น",
-        "การล่มสลายของราชวงศ์ชิง เมื่อพรรคคอมมิวนิสต์จีนเข้ามีอำนาจในจีน",
-        "แผ่นดินใหญ่เมื่อ พ.ศ. 2492 (1949) พรรคก๊กมินตั๋ง พรรคการเมือง",
-        "ชาตินิยมของจีนที่เป็นฝ่ายแพ้ก็พาผู้คนอพยพหนีออกจากแผ่นดินใหญ่มา",
-        "ตั้งหลักที่ไต้หวัน เพื่อวางแผนกลับไปครองอำนาจในจีนต่อไป\n",
-        "ชาวจีนมากกว่า 1 ล้าน 5 แสนคน อพยพตามมาอยู่ที่เกาะไต้หวันในยุคที่",
-        "เหมา เจ๋อตง มีอำนาจเต็มที่ในจีนแผ่นดินใหญ่ ผู้นำของประเทศทั้งสอง",
-        "จีนคือผู้นำพรรคคอมมิวนิสต์กับผู้นำสาธารณรัฐจีนบนเกาะไต้หวัน แย่ง",
-        "กันเป็นกระบอกเสียงของประชาชนจีนในเวทีโลก แต่เสียงของนานาประเทศ",
-        "ส่วนใหญ่เกรงอิทธิพลของจีนแผ่นดินใหญ่ จึงให้การยอมรับจีนแผ่นดิน",
-        "ใหญ่มากกว่า\n",
-        "ในปี พ.ศ. 2514 (ค.ศ. 1971) ก่อนที่นายพล เจียง ไคเช็ก",
-        "(ภาษาจีน: 蔣中正) จะถึงอสัญกรรมไม่กี่ปี สาธารณรัฐจีนซึ่งเป็น",
-        "ประเทศที่ร่วมก่อตั้งองค์การสหประชาชาติได้สูญเสียสมาชิกภาพใน",
-        "ฐานะตัวแทนชาวจีนให้กับสาธารณรัฐประชาชนจีน ในปี พ.ศ. 2521 (1978)",
-        "สหประชาชาติประกาศรับรองจีนเดียวคือจีนแผ่นดินใหญ่และตัดสัมพันธ์",
-        "ทางการเมืองกับสาธารณรัฐจีน ทั้งสหรัฐอเมริกาก็ได้ถอนการรับรองว่า",
-        "สาธารณรัฐจีนมีฐานะเป็นรัฐ ไต้หวันจึงกลายเป็นเพียงดินแดนที่จีน",
-        "อ้างว่าเป็นส่วนหนึ่งของสาธารณรัฐประชาชนจีนตั้งแต่นั้นเป็นต้นมา\n",
-        "เมื่อเจียง ไคเช็ก ถึงแก่อสัญกรรมในปี พ.ศ. 2518 (1975) ลูกชาย",
-        "ที่ชื่อ เจี่ยง จิงกั๋ว ได้เป็นผู้สืบทอดการปกครอง",
-        "ไต้หวันต่อและเริ่มกระบวนการ วางรากฐานไปสู่ประชาธิปไตย\n",
-        "หลังจากที่ประธานาธิบดี เจียง จิงกั๋ว เสียชีวิต ไต้หวันจึงได้เข้า",
-        "สู่ระบอบประชาธิปไตยเต็มรูปแบบ ประธานาธิบดีคนใหม่ ซึ่งเกิดใน",
-        "ไต้หวัน ชื่อ หลี่ เติงฮุย ขึ้นบริหารประเทศ โดยการสนับสนุนของ",
-        "เจี่ยง จิงกั๋ว ทั้งที่ หลี่ เติงฮุย นั้นเคลื่อนไหว",
-        "สนับสนุนเอกราชไต้หวัน นาย รัฐบาลจีนที่ปักกิ่งได้ตั้ง",
-        "ฉายาประธานาธิบดีไต้หวันคนใหม่ว่า \"จิ้งจกปากหวาน\" ",
-        "ช่วงเวลาที่นายหลี่ เติงฮุย เป็นประธานาธิบดี การเมืองของไต้หวัน",
-        "เกิดการแตกแยกออกเป็น 3 ฝ่ายคือ 1) พวกก๊กมินตั๋ง ที่ต้องการกลับ",
-        "ไปรวมประเทศกับจีนแผ่นดินใหญ่ (รวมจีนแผ่นดินใหญ่ภายใต้การปกครอง",
-        "ของสาธารณรัฐจีน) 2) พวกที่ต้องการให้ไต้หวันเป็นประเทศอิสระไม่",
-        "เกี่ยวข้องกับจีนแผ่นดินใหญ่ และ 3) พวกที่ต้องการดำรงฐานะของ",
-        "ประเทศไว้ดังเดิมต่อไป\n",
-        "ไต้หวันกับจีนแผ่นดินใหญ่นัดเจรจาหาทางออกของข้อขัดแย้งทางการเมือง",
-        "ครั้งแรกที่สิงคโปร์เมื่อปี พ.ศ. 2536 (ค.ศ. 1993) แต่ปรากฏว่าจีน",
-        "แผ่นดินใหญ่ประวิงเวลาลงนามในสัญญาหลายฉบับที่เป็นข้อตกลงร่วมกัน ",
-        "ทำให้ผลการเจรจาคราวนั้นไม่ก้าวหน้าไปถึงไหน ความสัมพันธ์ระหว่าง",
-        "สองจีนเลวร้ายลงทุกที เมื่อประธานาธิบดี หลี่ เติงฮุย เดินทางไป",
-        "เยือนสหรัฐอเมริกาและได้รับการยอมรับอย่างเอิกเกริก ทำให้จีนแผ่น",
-        "ดินใหญ่ไม่พอใจอย่างมาก จึงข่มขวัญไต้หวันกับประเทศที่ให้การสนับ",
-        "สนุนไต้หวัน ด้วยการทำการซ้อมรบขึ้นใกล้ ๆ เกาะไต้หวัน สหรัฐ",
-        "อเมริกาออกมาแสดงอาการปกป้องคุ้มครองไต้หวันด้วยการส่งกำลังกอง",
-        "เรือรบของสหรัฐฯ มาป้วนเปี้ยนอยู่ในน่านน้ำที่จีนซ้อมรบ\n",
-        "ขณะที่โลกกำลังล่อแหลมกับสถานการณ์ที่ตึงเครียดในน่านน้ำจีนมาก",
-        "ขึ้นทุกทีนั้น ไต้หวันก็จัดให้มีการเลือกตั้งครั้งใหม่ และในการ",
-        "เลือกตั้งครั้งใหม่นั้นเอง ไต้หวันก็ได้นายหลี่ เติงฮุย เป็น",
-        "ประธานาธิบดีอีกครั้ง\n",
-        "ไต้หวันเข้าสู่สภาวะวิกฤต เมื่อเกิดแผ่นดินไหวครั้งร้ายแรงที่สุดใน",
-        "ประวัติศาสตร์ในเดือนกันยายน พ.ศ. 2542 (ค.ศ. 1999) ทำให้ประชากร",
-        "ส่วนมากที่เป็นชาวพื้นเมืองเสียชีวิตไป 2,000 คน ทั้งเมืองมีแต่",
-        "เศษซากปรักหักพังจากภัยธรรมชาติ และช่วงนี้ไต้หวันต้องเผชิญความ",
-        "ยากลำบาก จีนแผ่นดินใหญ่ก็เพิ่มความกดดันไม่ให้นานาชาติ",
-        "เข้ามายุ่งเกี่ยวกับไต้หวันแม้ในยามคับขันเช่นนี้ โดยประกาศว่า ",
-        "หากมีประเทศใดจะเข้าไปให้ความช่วยเหลือไต้หวัน จะต้องได้รับอนุญาต",
-        "จากจีนก่อน ซึ่งคำประกาศของจีนแผ่นดินใหญ่สวนทางกับเมตตาธรรมของ",
-        "ประเทศทั่วโลกที่ต้องการให้ความช่วยเหลือไต้หวัน\n",
-        "เดือนมีนาคม พ.ศ. 2543 (ค.ศ. 2000) มีการเลือกตั้งใหม่ในไต้หวัน ",
-        "ชาวไต้หวันเลือกผู้แทนจากพรรคประชาธิปไตยก้าวหน้า คือ นายเฉิน สุย",
-        "เปี่ยน เป็นประธานาธิบดีคนใหม่ของไต้หวัน ผู้ประกาศนโยบายการเมือง",
-        "แข็งกร้าวว่าไต้หวันต้องการแยกตัวเป็นอิสระจากจีนแผ่นดินใหญ่ ยุติ",
-        "ยุคของพรรคชาตินิยมที่ยังฝักใฝ่แผ่นดินใหญ่อยู่ จีนแผ่นดินใหญ่จึง",
-        "ถือว่าเป็นกบฏต่อการปกครองของจีน เพราะแต่ไหนแต่ไร ไต้หวันไม่เคย",
-        "ประกาศอย่างเป็นทางการว่าเป็นประเทศอิสระแยกจากจีน และจีนพูดอยู่",
-        "เสมอว่าไต้หวันเป็นเด็กในปกครองที่ค่อนข้างจะหัวดื้อและเกเร หาก",
-        "ไต้หวันประกาศว่าเป็นอิสระจากจีนเมื่อใด จีนก็จะยกกำลังจัดการ",
-        "กับไต้หวันทันที\n",
-        "ในขณะที่ความสัมพันธ์ทางการเมืองระหว่างสองจีนในสายตาชาวโลก",
-        "เลวร้ายลง จีนทั้งสองกลับมีการติดต่อทางการค้ากันมากขึ้น มีการ",
-        "ผ่อนปรนอนุญาตให้ชาวไต้หวันเดินทางไปจีนแผ่นดินใหญ่เพื่อเยี่ยม",
-        "ญาติได้ เกิดปรากฏการณ์สำคัญคือนักธุรกิจไต้หวันหอบเงินทุนกว่า ",
-        "20,000 ล้านดอลลาร์สหรัฐ ไปลงทุนดำเนินธุรกิจทางตอนใต้ของจีน",
-        "แผ่นดินใหญ่ จนกระทั่งขณะนี้ชาวไต้หวันกลายเป็นนักลงทุนรายใหญ่",
-        "เป็นลำดับ 2 ของจีน\n",
-        "วันที่ 24 พฤษภาคม 2560 ศาลรัฐธรรมนูญวินิจฉัยว่ากฎหมายสมรส",
-        "ปัจจุบันในเวลานั้น ละเมิดรัฐธรรมนูญ โดยปฏิเสธสิทธิสมรสของคู่รัก",
-        "เพศเดียวกันชาวไต้หวัน ศาลวินิจฉัยว่าหากสภานิติบัญญัติไม่ผ่าน",
-        "การแก้ไขกฎหมายที่เพียงพอต่อกฎหมายสมรสของไต้หวันภายในสองปี ",
-        "การสมรสเพศเดียวกันจะชอบด้วยกฎหมายโดยอัตโนมัติในไต้หวัน[17] ",
-        "วันที่ 17 พฤษภาคม 2562 สภานิติบัญญัติไต้หวันอนุมัติ",
-        "ร่างกฎหมายทำให้การสมรสเพศเดียวกันชอบด้วยกฎหมาย",
-        " ทำให้เป็นประเทศแรกในทวีปเอเชียที่ผ่านกฎหมายดังกล่าว[18][19]"].join("");
-        let cargo_dir = PathBuf::from(CARGO_ROOT);
-        let dict_path = PathBuf::from(DEFAULT_DICT_PATH_RELATIVE_TO_ROOT);
-        let default_dict_path = cargo_dir.join(dict_path);
-        let default_dict =  create_custom_dict_trie(CustomDictSource::FilePath(default_dict_path));
-        let mut custom_input = CustomString::new(&long_text);
-        let result = NewmmCustom::internal_segment(&custom_input, &default_dict, false,true);
-        
-        assert_eq!(result.len(),1889);
-        
-}
-
diff --git a/rust_modules/src/tokenizer/dict_reader_custom.rs b/rust_modules/src/tokenizer/dict_reader_custom.rs
index 8378ad8..19804b4 100644
--- a/rust_modules/src/tokenizer/dict_reader_custom.rs
+++ b/rust_modules/src/tokenizer/dict_reader_custom.rs
@@ -7,7 +7,7 @@ use std::{
     path::PathBuf,
 };
 use super::super::fixed_bytes_str::four_bytes::{CustomString};
-const DEFAULT_DICT_FILE: &str = include_str!("../../../pythainlp/corpus/words_th.txt");
+const DEFAULT_DICT_FILE: &str = include_str!("../../../words_th.txt");
 pub enum DictSource {
     WordList(Vec<String>),
     FilePath(PathBuf),
diff --git a/rust_modules/src/tokenizer/tcc_custom.rs b/rust_modules/src/tokenizer/tcc_custom.rs
index 0cbba73..8b2587f 100644
--- a/rust_modules/src/tokenizer/tcc_custom.rs
+++ b/rust_modules/src/tokenizer/tcc_custom.rs
@@ -21,6 +21,8 @@ use ahash::{ AHashSet as HashSet};
 use lazy_static::lazy_static;
 use regex::bytes::Regex;
 
+use crate::fixed_bytes_str::four_bytes::{CustomString, CustomStringBytesSlice};
+
 use super::super::fixed_bytes_str::four_bytes::{BYTES_PER_CHAR};
 
 // regex crate does not support look-any-direction
@@ -67,7 +69,7 @@ lazy_static! {
 
 
 
-pub fn tcc_pos(custom_text_type:&[u8]) -> HashSet<usize> {
+pub fn tcc_pos(custom_text_type:&CustomStringBytesSlice) -> HashSet<usize> {
     let mut set: HashSet<usize> = HashSet::with_capacity(custom_text_type.len() / BYTES_PER_CHAR / 2);
     if custom_text_type.len() == 0 {
         set
@@ -84,7 +86,7 @@ pub fn tcc_pos(custom_text_type:&[u8]) -> HashSet<usize> {
     }
 }
 
-pub fn segment(custom_text_type:&[u8]) -> Vec<&[u8]> {
+pub fn segment(custom_text_type:&CustomStringBytesSlice) -> Vec<&CustomStringBytesSlice> {
     let mut txt  = custom_text_type.clone();
     let mut tcc_result: Vec<&[u8]> = Vec::with_capacity(txt.len() / 10);
     while txt.len() > 0 {
diff --git a/rust_modules/src/tokenizer/{61, 55, 49, 43, 37, 31, 86, 19, 80, 68, b/rust_modules/src/tokenizer/{61, 55, 49, 43, 37, 31, 86, 19, 80, 68,
deleted file mode 100644
index 9e7e1f6..0000000
--- a/rust_modules/src/tokenizer/{61, 55, 49, 43, 37, 31, 86, 19, 80, 68,
+++ /dev/null
@@ -1,2 +0,0 @@
-{61, 55, 49, 43, 37, 31, 86, 19, 80, 68, 62, 56, 50, 38, 32, 93, 26, 87, 20, 81, 75, 2, 63, 57, 51, 45, 39, 21, 15, 9, 70, 3, 64, 58, 52, 46, 40, 28, 89, 22, 16, 77, 71, 4, 65, 59, 53, 47, 41, 35, 23, 78, 11, 72, 5, 66, 60, 54, 48, 36, 30, 24, 85, 18, 79, 12, 6, 67}
-{2, 3, 4, 5, 6, 9, 11, 12, 15, 16, 18, 19, 20, 21, 22, 23, 24, 26, 28, 30, 31, 32, 35, 36, 37, 38, 39, 40, 41, 43, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 77, 78, 79, 80, 81, 85, 86, 87, 89, 93}
\ No newline at end of file


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..f0f7c87
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,45 @@
+name: CI
+on: [push, pull_request]
+
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-latest, windows-latest] # , macos-latest, windows-latest
+        python-version: [3.6, 3.7, 3.8]
+    steps:
+      - uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: stable
+          override: true
+
+      # Install dependencies on ubuntu
+      - if: matrix.os == 'ubuntu-latest'
+        name: Install build dependencies
+        run: sudo apt-get install -y libxml2-dev libxslt-dev
+
+      # Install 32-bit windows target for pypy3
+      - if: matrix.os == 'windows-latest' && matrix.python-version == 'pypy3'
+        name: Install 32-bit Rust target
+        uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: stable
+          override: true
+          target: i686-pc-windows-msvc
+      - name: Install
+        run: pip install maturin
+      - name: Build package
+        working-directory: ./rust_modules/
+        run: maturin build --release -i python
+      - name: Test
+        working-directory: ./rust_modules/
+        run: cargo test
diff --git a/rust_modules/tests/test_custom_string.rs b/rust_modules/tests/test_custom_string.rs
index 8f30955..c9a0e5e 100644
--- a/rust_modules/tests/test_custom_string.rs
+++ b/rust_modules/tests/test_custom_string.rs
@@ -1,32 +1,11 @@
-use pythainlp_rust_modules::{fixed_bytes_str::four_bytes::to_four_bytes, tokenizer::{ tcc,tcc_custom,trie,dict_reader::{DictSource,create_dict_trie}}};
-use regex::bytes::Regex;
-use std::{borrow::Borrow, path::{Path,PathBuf}};
-use std::env;
-use std::time::Instant;
-const DEFAULT_DICT_PATH_RELATIVE_TO_TEST:&str = "../pythainlp/corpus/words_th.txt"; 
-use pythainlp_rust_modules::fixed_bytes_str::four_bytes::CustomString;
+use oxidized_thainlp::{fixed_bytes_str::four_bytes::{CustomString}};
+
 
 const short_text_1:&str ="หมอนทองตากลมหูว์MBK39 :.ฉฺ๐๐๓-#™±";
 const short_text_2:&str = "ทดสอบ";
 
-// #[test]
-// fn test_tokenize_short_text(){
-//     let CURRENT_DIR = env::current_dir().unwrap();
-//     let dict_path = PathBuf::from(DEFAULT_DICT_PATH_RELATIVE_TO_TEST);
-//     let default_dict_path = CURRENT_DIR.join(dict_path);
-//     let default_dict =  create_dict_trie(DictSource::FilePath(default_dict_path));
-//     assert_eq!(Newmm::internal_segment("ฉันรักภาษาไทยเพราะฉันเป็นคนไทย", &default_dict, false), 
-//     ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"]);
-//     let problem_text = "ราชวงศ์ชิงพยายามลดการละเมิดสิทธิ์และความไม่ลงรอยกันในพื้นที่โดย
-//     ออกกฎหมายเพื่อจัดการตรวจ";
-//     let result = Newmm::internal_segment(problem_text, &default_dict, false);
-//     assert_eq!(result,["ราชวงศ์", "ชิง", "พยายาม", "ลด", "การ", "ละเมิดสิทธิ์", "และ", "ความ", "ไม่", "ลงรอยกัน", "ใน", "พื้นที่", "โดย", "\n", "    ", "ออกกฎหมาย", "เพื่อ", "จัดการ", "ตรวจ"]);
-//     println!("{:?}",result);
-    
-// }
 #[test]
 fn test_byte(){
-    // println!("{:?}",u8vec);
     let long_text = [ 
         "ไต้หวัน (แป่ะเอ๋ยี้: Tâi-oân; ไต่อวัน) หรือ ไถวาน ",
         "(อักษรโรมัน: Taiwan; จีนตัวย่อ: 台湾; จีนตัวเต็ม: 臺灣/台灣; พินอิน: ",
@@ -168,26 +147,9 @@ fn test_byte(){
         "วันที่ 17 พฤษภาคม 2562 สภานิติบัญญัติไต้หวันอนุมัติ",
         "ร่างกฎหมายทำให้การสมรสเพศเดียวกันชอบด้วยกฎหมาย",
         " ทำให้เป็นประเทศแรกในทวีปเอเชียที่ผ่านกฎหมายดังกล่าว[18][19]"].join("");
-        let time_for_conversion = Instant::now();
-        let our_str = CustomString::new(&long_text);
-        let std_str = our_str.to_std_string();
-
-        println!("time for conversion is {:?}",time_for_conversion.elapsed());
-        
-        let time_for_pos = Instant::now();
-        // let positions = tcc_custom::tcc_pos(&our_str);
-
+    let custom_string = CustomString::new(&long_text);
+    assert_eq!(custom_string.len() %4,0);
 
-//    println!("time for pos is {:?}",time_for_pos.elapsed());
-//    let time_for_pos = Instant::now();
-//    let positions_from_specialized = tcc_custom::tcc_pos(&our_str);
-//    let position_from_naive_implementation = tcc::tcc_pos(&long_text);
-//    let sengment_from_naive = tcc::segment(&long_text);
-// println!("time for specialized_pos is {:?}",time_for_pos.elapsed());
-// assert_eq!(positions,positions_from_specialized);
-// println!("{} {}",position_from_naive_implementation.len(),positions_from_specialized.len());
-// println!("{} {}",sengment_from_naive.len(),tcc_custom::specialized_newmm_segment(our_str.raw_content()).len());
-// assert_eq!(positions_from_specialized,position_from_naive_implementation);
 
  
 }
diff --git a/rust_modules/tests/test_tokenizer.rs b/rust_modules/tests/test_tokenizer.rs
index 45e94fb..c904664 100644
--- a/rust_modules/tests/test_tokenizer.rs
+++ b/rust_modules/tests/test_tokenizer.rs
@@ -1,4 +1,4 @@
-use oxidized_thainlp::{fixed_bytes_str::four_bytes::CustomString};
+use oxidized_thainlp::{fixed_bytes_str::four_bytes::CustomString, tokenizer::tokenizer_trait::Tokenizer};
 use oxidized_thainlp::tokenizer::{newmm_custom::{Newmm as NewmmCustom},dict_reader_custom::{DictSource as CustomDictSource,create_dict_trie as create_custom_dict_trie}};
 use std::{fs::canonicalize, path::{Path,PathBuf}};
 use std::env;
@@ -153,37 +153,14 @@ fn test_long_text_byte_tokenizer(){
         "วันที่ 17 พฤษภาคม 2562 สภานิติบัญญัติไต้หวันอนุมัติ",
         "ร่างกฎหมายทำให้การสมรสเพศเดียวกันชอบด้วยกฎหมาย",
         " ทำให้เป็นประเทศแรกในทวีปเอเชียที่ผ่านกฎหมายดังกล่าว[18][19]"].join("");
-        let loading_dict = Instant::now();
-        let CURRENT_DIR = env::current_dir().unwrap();
-        let dict_path = PathBuf::from(DEFAULT_DICT_PATH_RELATIVE_CARGO);
-        let cargo_path = PathBuf::from(CARGO_PATH);
-        let default_dict_path = cargo_path.join(dict_path);
-        let time_for_load_dict = Instant::now();
-        let default_dict =  create_custom_dict_trie(CustomDictSource::FilePath(default_dict_path.clone()));
-        println!("time for load dict is {:?}",time_for_load_dict.elapsed());
-        println!("{:?}",canonicalize(&default_dict_path) );
-        let mut custom_input = CustomString::new(&long_text);
-        let result = NewmmCustom::internal_segment(&custom_input, &default_dict.unwrap(), false,true);
+ 
+
+        let newmm_default_dict = NewmmCustom::new(None);
+        let result = newmm_default_dict.segment(&long_text, None,Some(true));
         
         let mut custom_input = CustomString::new(&long_text);
-        let safe_result = NewmmCustom::internal_segment(&custom_input, &default_dict.unwrap(), true,true);
+        let safe_result = newmm_default_dict.segment(&long_text, Some(true), Some(true));
         assert_eq!(result.len(),1889);
         assert_eq!(safe_result.len(),2011);
 }
 
-// #[test]
-// fn test_tokenize_non_thai(){
-//     let overall_time = Instant::now();
-//     let CURRENT_DIR = env::current_dir().unwrap();
-//     let dict_path = PathBuf::from(DEFAULT_DICT_PATH_RELATIVE_TO_TEST);
-//     let default_dict_path = CURRENT_DIR.join(dict_path);
-//     let default_dict =  create_dict_trie(DictSource::FilePath(default_dict_path));
-//     let result =  Newmm::internal_segment("ก12345 6789".to_string(), &default_dict, false);
-
-//     println!("{:?}",result);
-//     // println!("");
-//     // println!("{}",end_Time);
-//     // println!("{}",end_all);
-    
-  
-// }
\ No newline at end of file