├── .gitignore ├── .gitmodules ├── .idea ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── ottylab │ │ └── bitzenyminer │ │ └── MainActivityTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── ottylab │ │ │ └── bitzenyminer │ │ │ ├── MainActivity.java │ │ │ └── Utils.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── ottylab │ └── bitzenyminer │ └── UtilsUnitTest.java ├── bitzenymininglibrary ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── ottylab │ │ └── bitzenymininglibrary │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ └── bitzenymininglibrary.cpp │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── ottylab │ │ │ └── bitzenymininglibrary │ │ │ └── BitZenyMiningLibrary.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── example │ └── ottylab │ └── bitzenymininglibrary │ └── ExampleUnitTest.java ├── build.gradle ├── distribution ├── curl │ ├── include │ │ └── curl │ │ │ ├── curl.h │ │ │ ├── curlver.h │ │ │ ├── easy.h │ │ │ ├── mprintf.h │ │ │ ├── multi.h │ │ │ ├── stdcheaders.h │ │ │ ├── system.h │ │ │ └── typecheck-gcc.h │ ├── lib │ │ ├── arm64-v8a │ │ │ └── libcurl.a │ │ ├── armeabi-v7a │ │ │ └── libcurl.a │ │ ├── x86 │ │ │ └── libcurl.a │ │ └── x86_64 │ │ │ └── libcurl.a │ └── lib19 │ │ ├── armeabi-v7a │ │ └── libcurl.a │ │ └── x86 │ │ └── libcurl.a ├── jansson │ ├── include │ │ ├── jansson.h │ │ └── jansson_config.h │ ├── lib │ │ ├── arm64-v8a │ │ │ └── libjansson.a │ │ ├── armeabi-v7a │ │ │ └── libjansson.a │ │ ├── x86 │ │ │ └── libjansson.a │ │ └── x86_64 │ │ │ └── libjansson.a │ └── lib19 │ │ ├── armeabi-v7a │ │ └── libjansson.a │ │ └── x86 │ │ └── libjansson.a └── zlib │ ├── include │ ├── zconf.h │ └── zlib.h │ ├── lib │ ├── arm64-v8a │ │ └── libz.a │ ├── armeabi-v7a │ │ └── libz.a │ ├── x86 │ │ └── libz.a │ └── x86_64 │ │ └── libz.a │ └── lib19 │ ├── armeabi-v7a │ └── libz.a │ └── x86 │ └── libz.a ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/ 38 | 39 | # Keystore files 40 | # Uncomment the following line if you do not want to check your keystore files in. 41 | #*.jks 42 | 43 | # External native build folder generated in Android Studio 2.2 and later 44 | .externalNativeBuild 45 | 46 | # Google Services (e.g. APIs or Firebase) 47 | google-services.json 48 | 49 | # Freeline 50 | freeline.py 51 | freeline/ 52 | freeline_project_description.json 53 | 54 | # fastlane 55 | fastlane/report.xml 56 | fastlane/Preview.html 57 | fastlane/screenshots 58 | fastlane/test_output 59 | fastlane/readme.md 60 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bitzenymininglibrary/src/main/cpp/libcpuminer"] 2 | path = bitzenymininglibrary/src/main/cpp/libcpuminer 3 | url = https://github.com/OttyLab/cpuminer2.git 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BitZeny Miner for Android 2 | 3 | ## Summary 4 | This is a BitZeny Miner for Android based on [cpuminer](https://github.com/bitzeny/cpuminer). cpuminer is modifed to work as a library through JNI. 5 | 6 | ## Environment 7 | NDK r17b 8 | 9 | ## Library 10 | 11 | 12 | ### Constructors 13 | 14 | 15 | ``` 16 | public BitZenyMiningLibrary() 17 | ``` 18 | ``` 19 | public BitZenyMiningLibrary(Handler handler) 20 | ``` 21 | 22 | Library `putString` log strings with 'log' tag. Applications can `getString` then use the log strings. 23 | 24 | 25 | ### Methods 26 | 27 | ``` 28 | public boolean isMiningRunning() 29 | public int startMining(String url, String user, String password, int n_threads) 30 | public int startBenchmark(int n_threads) 31 | public int stopMining() 32 | ``` 33 | 34 | ## Tips 35 | 36 | Library core uses submodule. Therefore, run below commands after clone; 37 | 38 | ``` 39 | git submodule init 40 | git submodule update 41 | ``` 42 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.example.ottylab.bitzenyminer" 7 | minSdkVersion 21 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | flavorDimensions 'default' 20 | productFlavors { 21 | standard { 22 | } 23 | kitkat { 24 | minSdkVersion 19 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation project(':bitzenymininglibrary') 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation 'com.android.support:appcompat-v7:26.1.0' 33 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 34 | testImplementation 'junit:junit:4.12' 35 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 36 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 37 | androidTestImplementation 'com.microsoft.appcenter:espresso-test-extension:1.2' 38 | } 39 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/ottylab/bitzenyminer/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.example.ottylab.bitzenyminer; 2 | 3 | 4 | import android.support.test.espresso.ViewInteraction; 5 | import android.support.test.rule.ActivityTestRule; 6 | import android.support.test.runner.AndroidJUnit4; 7 | import android.test.suitebuilder.annotation.LargeTest; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.view.ViewParent; 11 | 12 | import com.microsoft.appcenter.espresso.Factory; 13 | import com.microsoft.appcenter.espresso.ReportHelper; 14 | 15 | import org.hamcrest.Description; 16 | import org.hamcrest.Matcher; 17 | import org.hamcrest.TypeSafeMatcher; 18 | import org.junit.After; 19 | import org.junit.Rule; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | 23 | import static android.support.test.espresso.Espresso.onView; 24 | import static android.support.test.espresso.action.ViewActions.click; 25 | import static android.support.test.espresso.assertion.ViewAssertions.matches; 26 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 27 | import static android.support.test.espresso.matcher.ViewMatchers.withClassName; 28 | import static android.support.test.espresso.matcher.ViewMatchers.withId; 29 | import static android.support.test.espresso.matcher.ViewMatchers.withText; 30 | import static org.hamcrest.Matchers.allOf; 31 | import static org.hamcrest.Matchers.containsString; 32 | import static org.hamcrest.Matchers.is; 33 | 34 | @LargeTest 35 | @RunWith(AndroidJUnit4.class) 36 | public class MainActivityTest { 37 | 38 | @Rule 39 | public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(MainActivity.class); 40 | 41 | @Rule 42 | public ReportHelper reportHelper = Factory.getReportHelper(); 43 | 44 | @After 45 | public void TearDown(){ 46 | reportHelper.label("Stopping App"); 47 | } 48 | 49 | @Test 50 | public void mainActivityTest() { 51 | ViewInteraction appCompatCheckBox = onView( 52 | allOf(withId(R.id.checkBoxBenchmark), withText("Benchmark"), 53 | childAtPosition( 54 | childAtPosition( 55 | withClassName(is("android.widget.LinearLayout")), 56 | 4), 57 | 1), 58 | isDisplayed())); 59 | appCompatCheckBox.perform(click()); 60 | 61 | ViewInteraction appCompatButton = onView( 62 | allOf(withId(R.id.buttonDrive), withText("Start"), 63 | childAtPosition( 64 | childAtPosition( 65 | withClassName(is("android.widget.LinearLayout")), 66 | 4), 67 | 0), 68 | isDisplayed())); 69 | appCompatButton.perform(click()); 70 | 71 | try{ 72 | Thread.sleep(5000); 73 | } catch (InterruptedException e){} 74 | 75 | ViewInteraction textView = onView( 76 | allOf(withId(R.id.textViewLog), 77 | childAtPosition( 78 | childAtPosition( 79 | withId(android.R.id.content), 80 | 0), 81 | 5), 82 | isDisplayed())); 83 | textView.check(matches(withText(containsString("miner threads started, using 'yescrypt' algorithm.")))); 84 | } 85 | 86 | private static Matcher childAtPosition( 87 | final Matcher parentMatcher, final int position) { 88 | 89 | return new TypeSafeMatcher() { 90 | @Override 91 | public void describeTo(Description description) { 92 | description.appendText("Child at position " + position + " in parent "); 93 | parentMatcher.describeTo(description); 94 | } 95 | 96 | @Override 97 | public boolean matchesSafely(View view) { 98 | ViewParent parent = view.getParent(); 99 | return parent instanceof ViewGroup && parentMatcher.matches(parent) 100 | && view.equals(((ViewGroup) parent).getChildAt(position)); 101 | } 102 | }; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ottylab/bitzenyminer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.ottylab.bitzenyminer; 2 | 3 | import android.content.SharedPreferences; 4 | import android.os.Build; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.preference.PreferenceManager; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.os.Bundle; 10 | import android.text.method.ScrollingMovementMethod; 11 | import android.util.Log; 12 | import android.view.View; 13 | import android.widget.AdapterView; 14 | import android.widget.ArrayAdapter; 15 | import android.widget.Button; 16 | import android.widget.CheckBox; 17 | import android.widget.EditText; 18 | import android.widget.Spinner; 19 | import android.widget.TextView; 20 | 21 | import com.example.ottylab.bitzenymininglibrary.BitZenyMiningLibrary; 22 | 23 | import java.lang.ref.WeakReference; 24 | import java.util.concurrent.BlockingQueue; 25 | import java.util.concurrent.LinkedBlockingQueue; 26 | 27 | public class MainActivity extends AppCompatActivity { 28 | private static final String TAG = "BitZenyMiner"; 29 | private static final int LOG_LINES = 1000; 30 | 31 | private BitZenyMiningLibrary miner; 32 | 33 | private EditText editTextServer; 34 | private EditText editTextUser; 35 | private EditText editTextPassword; 36 | private EditText editTextNThreads; 37 | private Button buttonDrive; 38 | private CheckBox checkBoxBenchmark; 39 | private Spinner spinnerAlgorithm; 40 | private TextView textViewLog; 41 | 42 | private boolean running; 43 | private BlockingQueue logs = new LinkedBlockingQueue<>(LOG_LINES); 44 | 45 | private static class JNICallbackHandler extends Handler { 46 | private final WeakReference activity; 47 | 48 | public JNICallbackHandler(MainActivity activity) { 49 | this.activity = new WeakReference(activity); 50 | } 51 | 52 | @Override 53 | public void handleMessage(Message msg) { 54 | MainActivity activity = this.activity.get(); 55 | if (activity != null) { 56 | String log = msg.getData().getString("log"); 57 | String logs = Utils.rotateStringQueue(activity.logs, log); 58 | activity.textViewLog.setText(logs); 59 | Log.d(TAG, log); 60 | } 61 | } 62 | } 63 | 64 | private static JNICallbackHandler sHandler; 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | setContentView(R.layout.activity_main); 70 | 71 | showDeviceInfo(); 72 | 73 | sHandler = new JNICallbackHandler(this); 74 | miner = new BitZenyMiningLibrary(sHandler); 75 | 76 | editTextServer = (EditText) findViewById(R.id.editTextServer); 77 | editTextServer.setOnFocusChangeListener(new View.OnFocusChangeListener() { 78 | @Override 79 | public void onFocusChange(View view, boolean b) { 80 | storeSetting(); 81 | } 82 | }); 83 | 84 | editTextUser = (EditText) findViewById(R.id.editTextUser); 85 | editTextUser.setOnFocusChangeListener(new View.OnFocusChangeListener() { 86 | @Override 87 | public void onFocusChange(View view, boolean b) { 88 | storeSetting(); 89 | } 90 | }); 91 | 92 | editTextPassword= (EditText) findViewById(R.id.editTextPassword); 93 | editTextPassword.setOnFocusChangeListener(new View.OnFocusChangeListener() { 94 | @Override 95 | public void onFocusChange(View view, boolean b) { 96 | storeSetting(); 97 | } 98 | }); 99 | 100 | editTextNThreads = (EditText) findViewById(R.id.editTextNThreads); 101 | editTextNThreads.setOnFocusChangeListener(new View.OnFocusChangeListener() { 102 | @Override 103 | public void onFocusChange(View view, boolean b) { 104 | storeSetting(); 105 | } 106 | }); 107 | 108 | buttonDrive = (Button) findViewById(R.id.buttonDrive); 109 | buttonDrive.setOnClickListener(new View.OnClickListener() { 110 | @Override 111 | public void onClick(View view) { 112 | if (running) { 113 | Log.d(TAG, "Stop"); 114 | miner.stopMining(); 115 | } else { 116 | Log.d(TAG, "Start"); 117 | int n_threads = 0; 118 | try { 119 | n_threads = Integer.parseInt(editTextNThreads.getText().toString()); 120 | } catch (NumberFormatException e){} 121 | 122 | BitZenyMiningLibrary.Algorithm algorithm = 123 | spinnerAlgorithm.getSelectedItemPosition() == 0 ? 124 | BitZenyMiningLibrary.Algorithm.YESCRYPT : BitZenyMiningLibrary.Algorithm.YESPOWER; 125 | if (checkBoxBenchmark.isChecked()) { 126 | miner.startBenchmark(n_threads, algorithm); 127 | } else { 128 | miner.startMining( 129 | editTextServer.getText().toString(), 130 | editTextUser.getText().toString(), 131 | editTextPassword.getText().toString(), 132 | n_threads, 133 | algorithm); 134 | } 135 | } 136 | 137 | changeState(!running); 138 | storeSetting(); 139 | } 140 | }); 141 | 142 | checkBoxBenchmark = (CheckBox) findViewById(R.id.checkBoxBenchmark); 143 | spinnerAlgorithm = (Spinner) findViewById(R.id.spinnerAlgorithm); 144 | ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.algorithms, android.R.layout.simple_spinner_item); 145 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 146 | spinnerAlgorithm.setAdapter(adapter); 147 | 148 | textViewLog = (TextView) findViewById(R.id.textViewLog); 149 | textViewLog.setMovementMethod(new ScrollingMovementMethod()); 150 | 151 | restoreSetting(); 152 | changeState(miner.isMiningRunning()); 153 | } 154 | 155 | private void changeState(boolean running) { 156 | buttonDrive.setText(running ? "Stop" : "Start"); 157 | disableSetting(running); 158 | this.running = running; 159 | } 160 | 161 | private void disableSetting(boolean running) { 162 | editTextServer.setEnabled(!running); 163 | editTextUser.setEnabled(!running); 164 | editTextPassword.setEnabled(!running); 165 | editTextNThreads.setEnabled(!running); 166 | spinnerAlgorithm.setEnabled(!running); 167 | } 168 | 169 | private void storeSetting() { 170 | SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); 171 | SharedPreferences.Editor editor = pref.edit(); 172 | editor.putString("server", editTextServer.getText().toString()); 173 | editor.putString("user", editTextUser.getText().toString()); 174 | editor.putString("password", editTextPassword.getText().toString()); 175 | editor.putString("n_threads", editTextNThreads.getText().toString()); 176 | editor.putInt("algorithm", spinnerAlgorithm.getSelectedItemPosition()); 177 | editor.commit(); 178 | } 179 | 180 | private void restoreSetting() { 181 | SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); 182 | editTextServer.setText(pref.getString("server", null)); 183 | editTextUser.setText(pref.getString("user", null)); 184 | editTextPassword.setText(pref.getString("password", null)); 185 | editTextNThreads.setText(pref.getString("n_threads", null)); 186 | spinnerAlgorithm.setSelection(pref.getInt("algorithm", 0)); 187 | } 188 | 189 | private void showDeviceInfo() { 190 | String[] keys = new String[]{ "os.arch", "os.name", "os.version" }; 191 | for (String key : keys) { 192 | Log.d(TAG, key + ": " + System.getProperty(key)); 193 | } 194 | Log.d(TAG, "CODE NAME: " + Build.VERSION.CODENAME); 195 | Log.d(TAG, "SDK INT: " + Build.VERSION.SDK_INT); 196 | Log.d(TAG, "MANUFACTURER: " + Build.MANUFACTURER); 197 | Log.d(TAG, "MODEL: " + Build.MODEL); 198 | Log.d(TAG, "PRODUCT: " + Build.PRODUCT); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ottylab/bitzenyminer/Utils.java: -------------------------------------------------------------------------------- 1 | package com.example.ottylab.bitzenyminer; 2 | 3 | import java.util.concurrent.BlockingQueue; 4 | 5 | /** 6 | * Created by ochikage on 2018/02/18. 7 | */ 8 | 9 | public class Utils { 10 | public static String rotateStringQueue(BlockingQueue queue, String next) { 11 | while (!queue.offer(next)) { 12 | try { 13 | queue.take(); 14 | } catch (InterruptedException e){ 15 | ; 16 | } 17 | } 18 | 19 | StringBuilder logs = new StringBuilder(); 20 | for (String element: queue) { 21 | logs.append(element); 22 | } 23 | 24 | return logs.toString(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 23 | 24 | 37 | 38 | 51 | 52 | 65 | 66 | 70 | 71 |