text:
# Awesome Active Learning [![Awesome](https://awesome.re/badge.svg)](https://awesome.re)

🤩 A curated list of awesome Active Learning ! 🤩

 Background

![image](./fig/an_illustrative_AL_example.jpg)

(`An illustrative example of pool-based active learning`. image source: [Settles, Burr](https://minds.wisconsin.edu/handle/1793/60660))

 What is Active Learning?

Active learning is a special case of machine learning in which a learning algorithm can interactively query a oracle (or some other information source) to label new data points with the desired outputs.

![image](./fig/active_learning_cycle.jpg)

(`The pool-based active learning cycle`. image source: [Settles, Burr](https://minds.wisconsin.edu/handle/1793/60660))

There are situations in which unlabeled data is abundant but manual labeling is expensive. In such a scenario, learning algorithms can actively query the oracle for labels. This type of iterative supervised learning is called active learning. Since the learner chooses the examples, the number of examples to learn a concept can often be much lower than the number required in normal supervised learning. With this approach, there is a risk that the algorithm is overwhelmed by uninformative examples. Recent developments are dedicated to multi-label active learning, hybrid active learning and active learning in a single-pass (on-line) context, combining concepts from the field of machine learning (e.g. conflict and ignorance) with adaptive, incremental learning policies in the field of online machine learning.

(source: [Wikipedia](https://en.wikipedia.org/wiki/Active_learning_(machine_learning)))

 Contributing

If you find the awesome paper/code/book/tutorial or have some suggestions, please feel free to [pull requests](https://github.com/baifanxxx/awesome-active-learning/pulls) or contact <baifanxxx@gmail.com> or <chenliangyudavid@gmail.com> to add papers using the following Markdown format:

``` txt
Year | Paper Name | Conference | [Paper](link) | [Code](link) | Tags | Notes |
```

 Tags

`Sur.`: survey                     |  `Cri.`: critics                    |
`Pool.`: pool-based sampling       |  `Str.`: stream-based sampling      |  `Syn.`: membership query synthesize  |
`Semi.`: semi-supervised learning  |  `Self.`: self-supervised learning  |  `RL.`: reinforcement learning        |
`FS.`: few-shot learning           |  `Meta.`: meta learning             |

Thanks for your valuable contribution to the research community. 😃

---

 Table of Contents

- [Books](#books)
- [Surveys](#surveys)
- [Papers](#papers)
  - [2022](#2022)
  - [2021](#2021)
  - [2020](#2020)
  - [2019](#2019)
  - [2018](#2018)
  - [2017](#2017)
  - [Before 2017](#before-2017)

- [Turtorials](#turtorials)
- [Tools](#tools)

---

# Books

* [Chapter 22 Active Learning: A Survey. from Data Classification: Algorithms and Applications](http://charuaggarwal.net/active-survey.pdf) Charu C. Aggarwa et al.(CRC Press, 2014)
* [Active Learning](https://www.morganclaypool.com/doi/abs/10.2200/S00429ED1V01Y201207AIM018). Burr Settles. (CMU, 2012)

# Surveys

| Year | Paper | Author | Publication | Code | Notes |
| --- | --- | :---: | :---: |  --- | --- |
| 2022 | [A Comparative Survey of Deep Active Learning](https://arxiv.org/pdf/2203.13450.pdf) | Xueying Zhan et al. | arXiv | [code](https://github.com/SineZHAN/deepALplus) |  |
| 2021 | [A Survey on Active Deep Learning: From Model-driven to Data-driven](https://arxiv.org/abs/2101.09933) | Peng Liu et al. | CSUR |  |  |
| 2020 | [A Survey of Active Learning for Text Classification using Deep Neural Networks](https://arxiv.org/abs/2008.07267) | Christopher Schröder et al. | arXiv || |
| 2020 | [A Survey of Deep Active Learning](https://arxiv.org/abs/2009.00236) | Pengzhen Ren et al. | CSUR |  |  |
| 2009 | [Active Learning Literature Survey](https://minds.wisconsin.edu/handle/1793/60660) | Settles, Burr. | University of Wisconsin-Madison Department of Computer Sciences |  |  |

# Papers

## 2022

| Title        | Publication |  Paper  |  Code  | Tags | Notes |
| --------     | :-----: |  :----: | :----: |----|----|
|Active Learning Helps Pretrained Models Learn the Intended Task|NeurIPS|[paper](https://arxiv.org/abs/2204.08491)|[code](https://github.com/alextamkin/active-learning-pretrained-models)|`Pool.`||
|Making Your First Choice: To Address Cold Start Problem in Vision Active Learning|NeurIPS workshop|[paper](https://arxiv.org/abs/2210.02442)|[code](https://github.com/c-liangyu/CSVAL)|`Pool.`| Cold-start problem|
|Active Learning Through a Covering Lens|NeurIPS|[paper](https://arxiv.org/abs/2205.11320)|[code](https://github.com/avihu111/TypiClust)|`Pool.`| |
|Active Surrogate Estimators: An Active Learning Approach to Label-Efficient Model Evaluation|NeurIPS|[paper](https://arxiv.org/pdf/2202.06881)|[code](https://github.com/jlko/active-surrogate-estimators)|`Pool.`|Model evaluation|
|Meta-Query-Net: Resolving Purity-Informativeness Dilemma in Open-set Active Learning|NeurIPS|[paper](https://arxiv.org/pdf/2210.07805.pdf)|[code](https://github.com/kaist-dmlab/MQNet)|`Pool.`||
|One-Bit Active Query With Contrastive Pairs|CVPR|[paper](https://openaccess.thecvf.com/content/CVPR2022/html/Zhang_One-Bit_Active_Query_With_Contrastive_Pairs_CVPR_2022_paper.html)|  |`Pool.`|One-bit supervision task|
|Active label cleaning for improved dataset quality under resource constraints |Nature Communications|[paper](https://arxiv.org/abs/2109.00574)|[code](https://github.com/microsoft/InnerEye-DeepLearning/tree/1606729c7a16e1bfeb269694314212b6e2737939/InnerEye-DataQuality)|`Pool.`|Label cleaning|
|Towards Fewer Annotations: Active Learning via Region Impurity and Prediction Uncertainty for Domain Adaptive Semantic Segmentation |CVPR|[paper](https://arxiv.org/abs/2111.12940)|[code](https://github.com/BIT-DA/RIPU)|`Pool.`||
|Budget-aware Few-shot Learning via Graph Convolutional Network |arXiv|[paper](https://arxiv.org/abs/2201.02304)||`Pool.` `Meta.` `FS.`||
|Using Self-Supervised Pretext Tasks for Active Learning |arXiv|[paper](https://arxiv.org/abs/2201.07459)|[code](https://github.com/johnsk95/PT4AL)|`Pool.` `SS.`|Cold-start problem|
|Low-Budget Active Learning via Wasserstein Distance: An Integer Programming Approach|ICLR|[paper](https://arxiv.org/abs/2106.02968)||`Pool.`| Cold-start problem|
|Active Learning by Feature Mixing|CVPR|[paper](https://arxiv.org/abs/2203.07034)|[code](https://github.com/AminParvaneh/alpha_mix_active_learning)|`Pool.`||
|Coherence-based Label Propagation over Time Series for Accelerated Active Learning|ICLR|[paper](https://openreview.net/forum?id=gjNcH0hj0LM)| [code](https://github.com/kaist-dmlab/TCLP) |`Pool.`|Time series|

## 2021

| Title        | Publication |  Paper  |  Code  | Tags | Notes |
| --------     | :-----: |  :----: | :----: |----|----|
| Active learning with MaskAL reduces annotation effort for training Mask R-CNN | arXiv | [paper](https://arxiv.org/abs/2112.06586) | [code](https://github.com/pieterblok/maskal) |  |  |
| MedSelect: Selective Labeling for Medical Image Classification Combining Meta-Learning with Deep Reinforcement Learning   |arXiv|[paper](https://arxiv.org/abs/2103.14339)| [code](https://github.com/stanfordmlgroup/MedSelect) |`Pool.` `Meta.` `RL.`|  |
| Can Active Learning Preemptively Mitigate Fairness Issues |ICLR-RAI|[paper](https://arxiv.org/abs/2104.06879)|[code](https://github.com/ElementAI/active-fairness)|`Pool.`|Thinking fairness issues|
|Sequential Graph Convolutional Network for Active Learning |CVPR|[paper](https://arxiv.org/pdf/2006.10219.pdf)|[code](https://github.com/razvancaramalau/Sequential-GCN-for-Active-Learning)|`Pool.`| |
|Task-Aware Variational Adversarial Active Learning |CVPR|[paper](https://arxiv.org/abs/2002.04709)|[code](https://github.com/cubeyoung/TA-VAAL)|`Pool.`| |
|Effective Evaluation of Deep Active Learning on Image Classification Tasks |arXiv|[paper](https://arxiv.org/abs/2106.15324)||`Cri.`| |
|Semi-Supervised Active Learning for Semi-Supervised Models: Exploit Adversarial Examples With Graph-Based Virtual Labels |ICCV|[paper](https://openaccess.thecvf.com/content/ICCV2021/papers/Guo_Semi-Supervised_Active_Learning_for_Semi-Supervised_Models_Exploit_Adversarial_Examples_With_ICCV_2021_paper.pdf)||`Pool.` `Semi.`| |
|Contrastive Coding for Active Learning under Class Distribution Mismatch |ICCV|[paper](https://openaccess.thecvf.com/content/ICCV2021/html/Du_Contrastive_Coding_for_Active_Learning_Under_Class_Distribution_Mismatch_ICCV_2021_paper.html)|[code](https://github.com/RUC-DWBI-ML/CCAL)|`Pool.`|Defines a good question|
|Mind Your Outliers! Investigating the Negative Impact of Outliers on Active Learning for Visual Question Answering |ACL-IJCNLP|[paper](https://arxiv.org/abs/2107.02331)|[code](https://github.com/siddk/vqa-outliers)|`Pool.`|Thinking about outliers |
|LADA: Look-Ahead Data Acquisition via Augmentation for Active Learning |NeurIPS|[paper](https://arxiv.org/abs/2011.04194)||`Pool.`| |
|Multi-Anchor Active Domain Adaptation for Semantic Segmentation |ICCV|[paper](https://arxiv.org/abs/2108.08012)|[code](https://github.com/munanning/mada)|`Pool.`| |
|Active Learning for Lane Detection: A Knowledge Distillation Approach |ICCV|[paper](https://openaccess.thecvf.com/content/ICCV2021/html/Peng_Active_Learning_for_Lane_Detection_A_Knowledge_Distillation_Approach_ICCV_2021_paper.html)||`Pool.`| |
|Active Contrastive Learning of Audio-Visual Video Representations |ICLR|[paper](https://arxiv.org/abs/2009.09805)|[code](https://github.com/yunyikristy/CM-ACC)|`Pool.`| |
|Multiple instance active learning for object detection |CVPR|[paper](https://arxiv.org/abs/2104.02324)|[code](https://github.com/yuantn/MI-AOD)|`Pool.`| |
|SEAL: Self-supervised Embodied Active Learning using Exploration and 3D Consistency |NeurIPS|[paper](https://arxiv.org/abs/2112.01001)||`Self.`|Robot exploration|
|Influence Selection for Active Learning |ICCV|[paper](https://openaccess.thecvf.com/content/ICCV2021/html/Liu_Influence_Selection_for_Active_Learning_ICCV_2021_paper.html)|[code](https://github.com/dragonlzm/ISAL)|`Pool.`||
|Reducing Label Effort: Self-Supervised meets Active Learning |arXiv|[paper](https://arxiv.org/abs/2108.11458)||`Pool.` `Self.` `Cri.`| A meaningful attempt on the combination of SS & AL|
|Towards General and Efficient Active Learning |arXiv|[paper](https://arxiv.org/abs/2112.07963)|[code](https://github.com/yichen928/GEAL_active_learning)|`Pool.` `Self.`| Single-pass AL based on SS ViT|
|Cartography Active Learning |EMNLP Findings|[paper](https://arxiv.org/abs/2109.04282)|[code](https://github.com/jjzha/cal)|`Pool.`| |
|Joint Semi-supervised and Active Learning for Segmentation of Gigapixel Pathology Images with Cost-Effective Labeling |ICCVW|[paper](https://openaccess.thecvf.com/content/ICCV2021W/CDPath/papers/Lai_Joint_Semi-Supervised_and_Active_Learning_for_Segmentation_of_Gigapixel_Pathology_ICCVW_2021_paper.pdf)||`Pool.`| |
|PAL : Pretext-based Active Learning|BMVC|[paper](https://arxiv.org/abs/2010.15947)|[code](https://github.com/shubhangb97/PAL_pretext_based_active_learning)|`Pool.`| Cold-start problem|
|Active Learning for Deep Object Detection via Probabilistic Modeling|ICCV|[paper](https://openaccess.thecvf.com/content/ICCV2021/html/Choi_Active_Learning_for_Deep_Object_Detection_via_Probabilistic_Modeling_ICCV_2021_paper.html)|[code](https://github.com/NVlabs/AL-MDN)|`Pool.`|GMM|
|Unsupervised Data Selection for Data-Centric Semi-Supervised Learning|arXiv|[paper](https://arxiv.org/abs/2110.03006)||`Pool.`|Data selection + SSL|
|Batch Active Learning at Scale|NeurIPS|[paper](https://arxiv.org/abs/2107.14263)||`Scale.` `Pool.`| |

## 2020

| Title        | Publication |  Paper  |  Code  | Tags | Notes |
| --------     | :-----: |  :----: | :----: |----|----|
| Contextual Diversity for Active Learning | ECCV | [paper](https://link.springer.com/chapter/10.1007/978-3-030-58517-4_9) | [code](https://github.com/sharat29ag/CDAL) | `Pool.`| |
| Active Learning for BERT: An Empirical Study | EMNLP | [paper](https://aclanthology.org/2020.emnlp-main.638/) | [code](https://github.com/IBM/low-resource-text-classification-framework) |`Pool.`| |
| Reinforced active learning for image segmentation |ICLR|[paper](https://arxiv.org/abs/2002.06583)|[code](https://github.com/ArantxaCasanova/ralis)|`Pool.` `RL.`| |
|Deep Batch Active Learning by Diverse, Uncertain Gradient Lower Bounds|ICLR|[paper](https://arxiv.org/abs/1906.03671)|[code](https://github.com/JordanAsh/badge)|`Pool.`| |
|Adversarial Sampling for Active Learning|WACV|[paper](https://openaccess.thecvf.com/content_WACV_2020/html/Mayer_Adversarial_Sampling_for_Active_Learning_WACV_2020_paper.html)||`Pool.`| |
|Online Active Learning of Reject Option Classifiers|AAAI|[paper](https://ojs.aaai.org/index.php/AAAI/article/view/6019/5875)||| |
| ViewAL: Active Learning with Viewpoint Entropy for Semantic Segmentation |CVPR|[paper](https://openaccess.thecvf.com/content_CVPR_2020/html/Siddiqui_ViewAL_Active_Learning_With_Viewpoint_Entropy_for_Semantic_Segmentation_CVPR_2020_paper.html)| [code](https://github.com/nihalsid/ViewAL) |`Pool.`| |
|Deep Active Learning for Biased Datasets via Fisher Kernel Self-Supervision|CVPR|[paper](http://openaccess.thecvf.com/content_CVPR_2020/papers/Gudovskiy_Deep_Active_Learning_for_Biased_Datasets_via_Fisher_Kernel_Self-Supervision_CVPR_2020_paper.pdf)| [code](https://github.com/gudovskiy/al-fk-self-supervision) || |
| Deep Reinforcement Active Learning for Medical Image Classification   |MICCAI|[paper](https://link.springer.com/chapter/10.1007/978-3-030-59710-8_4)||`Pool.` `RL.`| |
| State-Relabeling Adversarial Active Learning  |CVPR|[paper](https://openaccess.thecvf.com/content_CVPR_2020/html/Zhang_State-Relabeling_Adversarial_Active_Learning_CVPR_2020_paper.html)|[code](https://github.com/Beichen1996/SRAAL)|`Pool.`  | |
|Towards Robust and Reproducible Active Learning Using Neural Networks|arXiv|[paper](https://arxiv.org/pdf/2002.09564)| [code](https://github.com/acl21/deep-active-learning-pytorch) |`Cri.`| |
| Minimax Active Learning | arXiv |[paper](https://arxiv.org/pdf/2012.10467v2.pdf)|||  |
| Bayesian Force Fields from Active Learning for Simulation of Inter-Dimensional Transformation of Stanene | npj Computational Materials | [paper](https://arxiv.org/pdf/2008.11796v2.pdf) | [code](https://github.com/mir-group/flare) ||  |
|Consistency-Based Semi-supervised Active Learning: Towards Minimizing Labeling Cost|ECCV|[paper](https://link.springer.com/chapter/10.1007/978-3-030-58607-2_30)||`Pool.` `Semi.`| |
|Cold-start Active Learning through Self-supervised Language Modeling|EMNLP|[paper](https://arxiv.org/abs/2010.09535)| [code](https://github.com/forest-snow/alps) |`Pool.` `SS.`||

## 2019

| Title        | Publication |  Paper  |  Code  | Tags | Notes |
| --------     | :-----: |  :----: | :----: |----|----|
| Generative Adversarial Active Learning for Unsupervised Outlier Detection | TKDE | [paper](https://arxiv.org/pdf/1809.10816v4.pdf) | [code](https://github.com/leibinghe/GAAL-based-outlier-detection) |||
| Bayesian Generative Active Deep Learning    |ICML|[paper](http://proceedings.mlr.press/v97/tran19a.html)|[code](https://github.com/toantm/BGADL)|`Pool.` `Semi.`|   |
| Variational Adversarial Active Learning    |ICCV|[paper](https://openaccess.thecvf.com/content_ICCV_2019/html/Sinha_Variational_Adversarial_Active_Learning_ICCV_2019_paper.html)|[code](https://github.com/sinhasam/vaal)|`Pool.` `Semi.`|   |
|Integrating Bayesian and Discriminative Sparse Kernel Machines for Multi-class Active Learning|NeurIPS|[paper](https://papers.nips.cc/paper/2019/hash/bcc0d400288793e8bdcd7c19a8ac0c2b-Abstract.html)|| |   |
|Active Learning via Membership Query Synthesisfor Semi-supervised Sentence Classification|CoNLL|[paper](https://www.aclweb.org/anthology/K19-1044/)|| |   |
|Discriminative Active Learning|arXiv|[paper](https://arxiv.org/pdf/1907.06347.pdf)| [code](https://github.com/dsgissin/DiscriminativeActiveLearning) | |   |
|Semantic Redundancies in Image-Classification Datasets: The 10% You Don’t Need|arXiv|[paper](https://arxiv.org/pdf/1901.11409.pdf)|| |   |
|On-the-Fly Bayesian Active Learning of Interpretable Force-Fields for Atomistic Rare Events|npj Computational Materials|[paper](https://arxiv.org/pdf/1904.02042v2.pdf)| [code](https://github.com/mir-group/flare) | |   |
|Bayesian Batch Active Learning as Sparse Subset Approximation|NIPS|[paper](http://papers.nips.cc/paper/8865-bayesian-batch-active-learning-as-sparse-subset-approximation.pdf)| [code](https://github.com/rpinsler/active-bayesian-coresets) | |   |
| Learning Loss for Active Learning   |CVPR|[paper](https://openaccess.thecvf.com/content_CVPR_2019/html/Yoo_Learning_Loss_for_Active_Learning_CVPR_2019_paper.html)|[code](https://github.com/Mephisto405/Learning-Loss-for-Active-Learning)|`Pool.` |   |
|Rapid Performance Gain through Active Model Reuse|IJCAI|[paper](http://www.lamda.nju.edu.cn/liyf/paper/ijcai19-acmr.pdf)|| |   |
|Parting with Illusions about Deep Active Learning|arXiv|[paper](https://arxiv.org/abs/1912.05361)||`Cri.` |   |
|BatchBALD: Efficient and Diverse Batch Acquisition for Deep Bayesian Active Learning|NIPS|[paper](http://papers.nips.cc/paper/8925-batchbald-efficient-and-diverse-batch-acquisition-for-deep-bayesian-active-learning.pdf)| [code](https://github.com/BlackHC/BatchBALD) | |   |

## 2018

| Title        | Publication |  Paper  |  Code  | Tags | Notes |
| --------     | :-----: |  :----: | :----: |----|----|
|The Power of Ensembles for Active Learning in Image Classification|CVPR|[paper](http://openaccess.thecvf.com/content_cvpr_2018/papers/Beluch_The_Power_of_CVPR_2018_paper.pdf)||||
| Adversarial Learning for Semi-Supervised Semantic Segmentation |BMVC|[paper](https://arxiv.org/abs/1802.07934)|[code](https://github.com/hfslyc/AdvSemiSeg)|`Pool.` `Semi.`|  |
|A Variance Maximization Criterion for Active Learning|Pattern Recognition|[paper](https://www.sciencedirect.com/science/article/pii/S0031320318300256)| [code](https://github.com/YazhouTUD/MVAL) |||
|Meta-Learning Transferable Active Learning Policies by Deep Reinforcement Learning|ICLR-WS|[paper](https://arxiv.org/abs/1806.04798)||`Pool.` `Meta.` `RL.`||
|Active Learning for Convolutional Neural Networks: A Core-Set Approach|ICLR|[paper](https://openreview.net/pdf?id=H1aIuk-RW)||||
|Adversarial Active Learning for Sequence Labeling and Generation|IJCAI|[paper](https://www.ijcai.org/proceedings/2018/0558.pdf)||||
|Meta-Learning for Batch Mode Active Learning|ICLR-WS|[paper](https://openreview.net/references/pdf?id=r1PsGFJPz)||||
|Adversarial Active Learning for Deep Networks: a Margin Based Approach | ICML | [paper](https://arxiv.org/abs/1802.09841) |  |  |  |
|CEREALS - Cost-Effective REgion-based Active Learning for Semantic Segmentation | BMVC | [paper](https://arxiv.org/abs/1810.09726) |  |  |  |

## 2017

| Title        | Publication |  Paper  |  Code  | Tags | Notes |
| --------     | :-----: |  :----: | :----: |----|----|
|Active Decision Boundary Annotation with Deep Generative Models|ICCV|[paper](http://openaccess.thecvf.com/content_ICCV_2017/papers/Huijser_Active_Decision_Boundary_ICCV_2017_paper.pdf)| [code](https://github.com/MiriamHu/ActiveBoundary) |||
| Active One-shot Learning         |CoRR|[paper](https://arxiv.org/abs/1702.06559)|[code](https://github.com/markpwoodward/active_osl)|`Str.` `RL.` `FS.`|     |
| A Meta-Learning Approach to One-Step Active-Learning         |AutoML@PKDD/ECML|[paper](https://arxiv.org/abs/1706.08334)||`Pool.` `Meta.`|     |
| Generative Adversarial Active Learning         |arXiv|[paper](https://arxiv.org/abs/1702.07956)||`Pool.` `Syn.`|     |
|Active Learning from Peers|NIPS|[paper](http://papers.neurips.cc/paper/7276-active-learning-from-peers.pdf)||||
|Learning Active Learning from Data|NIPS|[paper](https://arxiv.org/abs/1703.03365)|[code](https://github.com/ksenia-konyushkova/LAL)|`Pool.`||
|Learning Algorithms for Active Learning|ICML|[paper](http://proceedings.mlr.press/v70/bachman17a.html)||||
| Deep Bayesian Active Learning with Image Data|ICML|[paper](http://proceedings.mlr.press/v70/gal17a)|[code](https://github.com/Riashat/Active-Learning-Bayesian-Convolutional-Neural-Networks/tree/master/ConvNets/FINAL_Averaged_Experiments/Final_Experiments_Run)|`Pool.`   |     |
|Learning how to Active Learn: A Deep Reinforcement Learning Approach|EMNLP|[paper](https://arxiv.org/abs/1708.02383)|[code](https://github.com/mengf1/PAL)|`Str.` `RL.`||

## Before 2017

|Year| Title        | Publication |  Paper  |  Code  | Tags | Notes |
|----| --------     | :-----: |  :----: | :----: |----|----|
|2016|Active Image Segmentation Propagation|CVPR|[paper](http://openaccess.thecvf.com/content_cvpr_2016/papers/Jain_Active_Image_Segmentation_CVPR_2016_paper.pdf)||   |     |
|2016|Cost-Effective Active Learning for Deep Image Classification|TCSVT|[paper](https://arxiv.org/pdf/1701.03551.pdf)| [code](https://github.com/dhaalves/CEAL_keras) |   |     |
|2015|Multi-Label Active Learning from Crowds| arXiv |[paper](https://arxiv.org/pdf/1508.00722v1.pdf)||   |     |
|2015|Active Learning by Learning | AAAI | [paper](https://ojs.aaai.org/index.php/AAAI/article/view/9597) |  |  |  |
|2014|Beyond Disagreement-based Agnostic Active Learning|NIPS|[paper](https://arxiv.org/abs/1407.2657)||   |     |
|2014|Active Semi-Supervised Learning Using Sampling Theory for Graph Signals|KDD|[paper](https://dl.acm.org/doi/abs/10.1145/2623330.2623760)| [code](https://github.com/broshanfekr/Active_Semi-Supervised_Learning_Using_Sampling_Theory_for_Graph_signals) |   |     |
|2013|Active Learning for Probabilistic Hypotheses Usingthe Maximum Gibbs Error Criterion|NIPS|[paper](https://eprints.qut.edu.au/114032/)||   |     |
|2013|Active Learning for Multi-Objective Optimization|ICML|[paper](http://proceedings.mlr.press/v28/zuluaga13.html)||   |     |
|2012|Batch Active Learning via Coordinated Matching|ICML|[paper](https://arxiv.org/abs/1206.6458)||   |     |
|2012|Bayesian Optimal Active Search and Surveying|ICML|[paper](https://arxiv.org/abs/1206.6406)| [code](https://github.com/rmgarnett/active_search)|   |     |
|2011|Active Learning Using On-line Algorithms|KDD|[paper](https://dl.acm.org/doi/abs/10.1145/2020408.2020553)||   |     |
|2011|Bayesian Active Learning for Classification and Preference Learning|CoRR|[paper](https://arxiv.org/abs/1112.5745)| [code](https://github.com/cambridge-mlg/BALaudiogram)|   |     |
|2011|Active Learning from Crowds|ICML|[paper](https://openreview.net/pdf?id=yVemp8x6Av3y)||   |     |
|2011|Ask Me Better Questions: Active Learning Queries Based on Rule Induction|KDD|[paper](https://dl.acm.org/doi/abs/10.1145/2020408.2020559)||   |     |
|2010|Active Instance Sampling via Matrix Partition|NIPS|[paper](http://people.scs.carleton.ca/~yuhongguo/research/papers/activenips10figs.pdf)||   |     |
|2008|Hierarchical Sampling for Active Learning|ICML|[paper](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.92.8661&rep=rep1&type=pdf)||   |  |
|2008|An Analysis of Active Learning Strategies for Sequence Labeling Tasks|EMNLP|[paper](https://www.aclweb.org/anthology/D08-1112.pdf)||   |     |
|2008|Active Learning with Direct Query Construction|KDD|[paper](https://dl.acm.org/doi/abs/10.1145/1401890.1401950)||   |  |
|2007|Discriminative Batch Mode Active Learning|NIPS|[paper](https://dl.acm.org/doi/pdf/10.1145/1390156.1390183)| [code](https://github.com/dsgissin/DiscriminativeActiveLearning) |   |     |
|1994|Improving Generalization with Active Learning|Machine Learning|[paper](https://link.springer.com/content/pdf/10.1007/BF00993277.pdf)||   |     |

# Turtorials

* [Overview of Active Learning for Deep Learning](https://jacobgil.github.io/deeplearning/activelearning). Jacob Gildenblat.
* [Active Learning from Theory to Practice](https://www.youtube.com/watch?v=_Ql5vfOPxZU). Steve Hanneke, Robert Nowak. (ICML, 2019)

# Tools
* [Active-Learning-as-a-Service: An Efficient MLOps System for Data-Centric AI](https://github.com/MLSysOps/Active-Learning-as-a-Service). Huang, Yizheng and Zhang, Huaizheng and Li, Yuanming and Lau, Chiew Tong and You, Yang. (2022)
* [[BAAL] A Bayesian Active Learning Library](https://github.com/ElementAI/baal/). Atighehchian, Parmida and Branchaud-Charron, Frederic and Freyberg, Jan and Pardinas, Rafael and Schell, Lorne. (2019)
* [ALiPy: Active Learning in Python](https://github.com/NUAA-AL/alipy). Ying-Peng Tang, Guo-Xiang Li, Sheng-Jun Huang. (NUAA, 2019)
* [modAL: A modular active learning framework for Python](https://github.com/modAL-python/modAL). Tivadar Danka and Peter Horvath. (2018)

repo_name: baifanxxx/awesome-active-learning


text:
#include <assert.h>
#include <nusys.h>

#include "graphic.h"
#include "main.h"
#include "stage00.h"
#include "n64logo.h"

Vec3d cameraPos = {-200.0f, -200.0f, -200.0f};
Vec3d cameraTarget = {0.0f, 0.0f, 0.0f};
Vec3d cameraUp = {0.0f, 1.0f, 0.0f};

// the positions of the squares we're gonna draw
#define NUM_SQUARES 5
struct Vec3d squares[NUM_SQUARES] = {
  {0.0f, 0.0f, 0.0f},
  {0.0f, 0.0f, 0.0f},
  {0.0f, 0.0f, 0.0f},
  {0.0f, 0.0f, 200.0f},
  {0.0f, 0.0f, -100.0f},
};

int squaresRotations[NUM_SQUARES];
int initSquaresRotations[NUM_SQUARES] = {
  0,
  20,
  40,
  40,
  40,
};

// this is a boolean but the older version of C used by the N64 compiler
// (roughly C89), doesn't have a bool type, so we just use integers
int squaresRotationDirection;

int showN64Logo;

// the 'setup' function
void initStage00() {
  // the advantage of initializing these values here, rather than statically, is
  // that if you switch stages/levels, and later return to this stage, you can
  // call this function to reset these values.
  squaresRotationDirection = FALSE;
  showN64Logo = FALSE;

  // In the older version of C used by the N64 compiler (roughly C89), variables
  // must be declared at the top of a function or block scope. This is an example
  // of using block scope to declare a variable in the middle of a function.
  {
    int i;
    for (i = 0; i < NUM_SQUARES; ++i) {
      squaresRotations[i] = initSquaresRotations[i];
    }
  }
}

// the 'update' function
void updateGame00() {
  // read controller input from controller 1 (index 0)
  nuContDataGetEx(contdata, 0);
  // We check if the 'A' Button was pressed using a bitwise AND with
  // contdata[0].trigger and the A_BUTTON constant.
  // The contdata[0].trigger property is set only for the frame that the button is
  // initially pressed. The contdata[0].button property is similar, but stays on
  // for the duration of the button press.
  if (contdata[0].trigger & A_BUTTON){
    // when A button is pressed, reverse rotation direction
    squaresRotationDirection = !squaresRotationDirection;
  }

  if (contdata[0].button & B_BUTTON){
    // when B button is held, change squares into n64 logos
    showN64Logo = TRUE;
  } else {
    showN64Logo = FALSE;
  }

  // update square rotations
  {
    int i;
    for (i = 0; i < NUM_SQUARES; ++i)
    {
      squaresRotations[i] = (squaresRotations[i] + (squaresRotationDirection ? 1 : -1)) % 360;
    }
  }
}

// the 'draw' function
void makeDL00() {
  unsigned short perspNorm;
  GraphicsTask * gfxTask;

  // switch the current graphics task
  // also updates the displayListPtr global variable
  gfxTask = gfxSwitchTask();

  // prepare the RCP for rendering a graphics task
  gfxRCPInit();

  // clear the color framebuffer and Z-buffer, similar to glClear()
  gfxClearCfb();

  // initialize the projection matrix, similar to glPerspective() or glm::perspective()
  guPerspective(&gfxTask->projection, &perspNorm, FOVY, ASPECT, NEAR_PLANE,
                FAR_PLANE, 1.0);

  // Our first actual displaylist command. This writes the command as a value at
  // the tail of the current display list, and we increment the display list
  // tail pointer, ready for the next command to be written.
  // As for what this command does... it's just required when using a perspective
  // projection. Try pasting 'gSPPerspNormalize' into google if you want more
  // explanation, as all the SDK documentation has been placed online by
  // hobbyists and is well indexed.
  gSPPerspNormalize(displayListPtr++, perspNorm);

  // initialize the modelview matrix, similar to gluLookAt() or glm::lookAt()
  guLookAt(&gfxTask->modelview, cameraPos.x, cameraPos.y,
           cameraPos.z, cameraTarget.x, cameraTarget.y,
           cameraTarget.z, cameraUp.x, cameraUp.y, cameraUp.z);

  // load the projection matrix into the matrix stack.
  // given the combination of G_MTX_flags we provide, effectively this means
  // "replace the projection matrix with this new matrix"
  gSPMatrix(
    displayListPtr++,
    // we use the OS_K0_TO_PHYSICAL macro to convert the pointer to this matrix
    // into a 'physical' address as required by the RCP
    OS_K0_TO_PHYSICAL(&(gfxTask->projection)),
    // these flags tell the graphics microcode what to do with this matrix
    // documented here: http://n64devkit.square7.ch/tutorial/graphics/1/1_3.htm
    G_MTX_PROJECTION | // using the projection matrix stack...
    G_MTX_LOAD | // don't multiply matrix by previously-top matrix in stack
    G_MTX_NOPUSH // don't push another matrix onto the stack before operation
  );

  gSPMatrix(displayListPtr++,
    OS_K0_TO_PHYSICAL(&(gfxTask->modelview)),
    // similarly this combination means "replace the modelview matrix with this new matrix"
    G_MTX_MODELVIEW | G_MTX_NOPUSH | G_MTX_LOAD
  );

  {
    int i;
    Vec3d* square;
    for (i = 0; i < NUM_SQUARES; ++i)
    {
      square = &squares[i];
      // create a transformation matrix representing the position of the square
      guPosition(
        &gfxTask->objectTransforms[i],
        // rotation
        squaresRotations[i], // roll
        0.0f, // pitch
        0.0f, // heading
        1.0f, // scale
        // position
        square->x, square->y, square->z
      );

      // push relative transformation matrix
      gSPMatrix(displayListPtr++,
        OS_K0_TO_PHYSICAL(&(gfxTask->objectTransforms[i])),
        G_MTX_MODELVIEW | // operating on the modelview matrix stack...
        G_MTX_PUSH | // ...push another matrix onto the stack...
        G_MTX_MUL // ...which is multipled by previously-top matrix (eg. a relative transformation)
      );

      if (showN64Logo) {
        drawN64Logo();
      } else {
        drawSquare();
      }

      // pop the matrix that we added back off the stack, to move the drawing position
      // back to where it was before we rendered this object
      gSPPopMatrix(displayListPtr++, G_MTX_MODELVIEW);
    }
  }

  // mark the end of the display list
  gDPFullSync(displayListPtr++);
  gSPEndDisplayList(displayListPtr++);

  // assert that the display list isn't longer than the memory allocated for it,
  // otherwise we would have corrupted memory when writing it.
  // isn't unsafe memory access fun?
  // this could be made safer by instead asserting on the displaylist length
  // every time the pointer is advanced, but that would add some overhead.
  assert(displayListPtr - gfxTask->displayList < MAX_DISPLAY_LIST_COMMANDS);

  // create a graphics task to render this displaylist and send it to the RCP
  nuGfxTaskStart(
    gfxTask->displayList,
    (int)(displayListPtr - gfxTask->displayList) * sizeof (Gfx),
    NU_GFX_UCODE_F3DEX, // load the 'F3DEX' version graphics microcode, which runs on the RCP to process this display list
    NU_SC_SWAPBUFFER // tells NuSystem to immediately display the frame on screen after the RCP finishes rendering it
  );
}

// A static array of model vertex data.
// This include the position (x,y,z), texture U,V coords (called S,T in the SDK)
// and vertex color values in r,g,b,a form.
// As this data will be read by the RCP via direct memory access, which is
// required to be 16-byte aligned, it's a good idea to annotate it with the GCC
// attribute `__attribute__((aligned (16)))`, to force it to be 16-byte aligned.
Vtx squareVerts[] __attribute__((aligned (16))) = {
  //  x,   y,  z, flag, S, T,    r,    g,    b,    a
  { -64,  64, -5,    0, 0, 0, 0x00, 0xff, 0x00, 0xff  },
  {  64,  64, -5,    0, 0, 0, 0x00, 0x00, 0x00, 0xff  },
  {  64, -64, -5,    0, 0, 0, 0x00, 0x00, 0xff, 0xff  },
  { -64, -64, -5,    0, 0, 0, 0xff, 0x00, 0x00, 0xff  },
};

void drawSquare() {
  // load vertex data for the triangles
  gSPVertex(displayListPtr++, &(squareVerts[0]), 4, 0);
  // depending on which graphical features, the RDP might need to spend 1 or 2
  // cycles to render a primitive, and we need to tell it which to do
  gDPSetCycleType(displayListPtr++, G_CYC_1CYCLE);
  // use antialiasing, rendering an opaque surface
  gDPSetRenderMode(displayListPtr++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2);
  // reset any rendering flags set when drawing the previous primitive
  gSPClearGeometryMode(displayListPtr++,0xFFFFFFFF);
  // enable smooth (gourad) shading and z-buffering
  gSPSetGeometryMode(displayListPtr++, G_SHADE | G_SHADING_SMOOTH | G_ZBUFFER);

  // actually draw the triangles, using the specified vertices
  gSP2Triangles(displayListPtr++,0,1,2,0,0,2,3,0);

  // Mark that we've finished sending commands for this particular primitive.
  // This is needed to prevent race conditions inside the rendering hardware in
  // the case that subsequent commands change rendering settings.
  gDPPipeSync(displayListPtr++);
}

// this is an example of rendering a model defined as a set of static display lists
void drawN64Logo() {
  gDPSetCycleType(displayListPtr++, G_CYC_1CYCLE);
  gDPSetRenderMode(displayListPtr++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2);
  gSPClearGeometryMode(displayListPtr++,0xFFFFFFFF);
  gSPSetGeometryMode(displayListPtr++, G_SHADE | G_SHADING_SMOOTH | G_ZBUFFER);

  // The gSPDisplayList command causes the RCP to render a static display list,
  // then return to this display list afterwards. These 4 display lists are
  // defined in n64logo.h, and were generated from a 3D model using a conversion
  // script.
  gSPDisplayList(displayListPtr++, N64Yellow_PolyList);
  gSPDisplayList(displayListPtr++, N64Red_PolyList);
  gSPDisplayList(displayListPtr++, N64Blue_PolyList);
  gSPDisplayList(displayListPtr++, N64Green_PolyList);

  gDPPipeSync(displayListPtr++);
}

// the nusystem callback for the stage, called once per frame
void stage00(int pendingGfx)
{
  // produce a new displaylist (unless we're running behind, meaning we already
  // have the maximum queued up)
  if(pendingGfx < 1)
    makeDL00();

  // update the state of the world for the next frame
  updateGame00();
}

repo_language: Objective-C


text:
Vtx N64Yellow_VertList[] = {
   { -44, -22, -42, 0, 0, 0, 245, 168, 5, 255},
   { -24, -43, -42, 0, 0, 0, 245, 168, 5, 255},
   { -44, -43, -42, 0, 0, 0, 245, 168, 5, 255},
   { -24, -43, -42, 0, 0, 0, 245, 168, 5, 255},
   { -44, -22, -42, 0, 0, 0, 245, 168, 5, 255},
   { -24, -22, -42, 0, 0, 0, 245, 168, 5, 255},
   { -24, 43, -42, 0, 0, 0, 245, 168, 51, 255},
   { -44, 24, -42, 0, 0, 0, 245, 168, 52, 255},
   { -44, 43, -42, 0, 0, 0, 246, 172, 66, 255},
   { -44, 24, -42, 0, 0, 0, 245, 168, 52, 255},
   { -24, 43, -42, 0, 0, 0, 245, 168, 51, 255},
   { -24, 24, -42, 0, 0, 0, 245, 170, 62, 255},
   { 23, 43, -42, 0, 0, 0, 245, 168, 5, 255},
   { 48, 24, -42, 0, 0, 0, 245, 168, 5, 255},
   { 23, 24, -42, 0, 0, 0, 245, 168, 5, 255},
   { 48, 24, -42, 0, 0, 0, 245, 168, 5, 255},
   { 23, 43, -42, 0, 0, 0, 245, 168, 5, 255},
   { 48, 43, -42, 0, 0, 0, 245, 168, 5, 255},
   { 23, -24, -42, 0, 0, 0, 245, 168, 5, 255},
   { 48, -43, -42, 0, 0, 0, 245, 168, 20, 255},
   { 23, -43, -42, 0, 0, 0, 245, 168, 6, 255},
   { 48, -43, -42, 0, 0, 0, 245, 168, 20, 255},
   { 23, -24, -42, 0, 0, 0, 245, 168, 5, 255},
   { 48, -24, -42, 0, 0, 0, 245, 168, 16, 255},
   { -44, -43, 42, 0, 0, 0, 245, 168, 5, 255},
   { -24, -24, 42, 0, 0, 0, 245, 168, 5, 255},
   { -44, -24, 42, 0, 0, 0, 245, 168, 5, 255},
   { -24, -24, 42, 0, 0, 0, 245, 168, 5, 255},
   { -44, -43, 42, 0, 0, 0, 245, 168, 5, 255},
   { -24, -43, 42, 0, 0, 0, 245, 168, 5, 255},
   { -44, 43, 42, 0, 0, 0, 245, 168, 5, 255},
   { -24, 24, 42, 0, 0, 0, 245, 168, 5, 255},
   { -24, 43, 42, 0, 0, 0, 245, 168, 5, 255},
   { -24, 24, 42, 0, 0, 0, 245, 168, 5, 255},
   { -44, 43, 42, 0, 0, 0, 245, 168, 5, 255},
   { -44, 24, 42, 0, 0, 0, 245, 168, 5, 255},
   { 23, -43, 42, 0, 0, 0, 245, 168, 5, 255},
   { 48, -24, 42, 0, 0, 0, 245, 168, 5, 255},
   { 23, -24, 42, 0, 0, 0, 245, 168, 5, 255},
   { 48, -24, 42, 0, 0, 0, 245, 168, 5, 255},
   { 23, -43, 42, 0, 0, 0, 245, 168, 5, 255},
   { 48, -43, 42, 0, 0, 0, 245, 168, 5, 255},
   { 23, 24, 42, 0, 0, 0, 245, 168, 5, 255},
   { 48, 43, 42, 0, 0, 0, 245, 168, 5, 255},
   { 23, 43, 42, 0, 0, 0, 245, 168, 5, 255},
   { 48, 43, 42, 0, 0, 0, 245, 168, 5, 255},
   { 23, 24, 42, 0, 0, 0, 245, 168, 5, 255},
   { 48, 24, 42, 0, 0, 0, 245, 168, 5, 255},
};

Gfx N64Yellow_PolyList[] = {
   gsSPVertex(N64Yellow_VertList+0,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPVertex(N64Yellow_VertList+30,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSPEndDisplayList(),
};

Vtx N64Red_VertList[] = {
   { 23, -43, -12, 0, 0, 0, 224, 55, 58, 255},
   { -24, -24, 42, 0, 0, 0, 222, 10, 20, 255},
   { -24, -43, 42, 0, 0, 0, 222, 10, 20, 255},
   { -24, -24, 42, 0, 0, 0, 222, 10, 20, 255},
   { 23, -43, -12, 0, 0, 0, 224, 55, 58, 255},
   { 23, -24, -12, 0, 0, 0, 222, 10, 20, 255},
   { -44, 24, 12, 0, 0, 0, 222, 10, 20, 255},
   { -24, -22, -42, 0, 0, 0, 222, 10, 20, 255},
   { -44, -22, -42, 0, 0, 0, 222, 10, 20, 255},
   { -24, -22, -42, 0, 0, 0, 222, 10, 20, 255},
   { -44, 24, 12, 0, 0, 0, 222, 10, 20, 255},
   { -24, 24, 12, 0, 0, 0, 222, 10, 20, 255},
   { -24, 43, -12, 0, 0, 0, 222, 11, 20, 255},
   { 23, 24, 42, 0, 0, 0, 222, 10, 20, 255},
   { 23, 43, 42, 0, 0, 0, 222, 10, 20, 255},
   { 23, 24, 42, 0, 0, 0, 222, 10, 20, 255},
   { -24, 43, -12, 0, 0, 0, 222, 11, 20, 255},
   { -24, 24, -12, 0, 0, 0, 222, 10, 20, 255},
   { 23, 24, -42, 0, 0, 0, 222, 10, 20, 255},
   { 48, -24, 13, 0, 0, 0, 222, 10, 20, 255},
   { 23, -24, 13, 0, 0, 0, 222, 10, 20, 255},
   { 48, -24, 13, 0, 0, 0, 222, 10, 20, 255},
   { 23, 24, -42, 0, 0, 0, 222, 10, 20, 255},
   { 48, 24, -42, 0, 0, 0, 222, 10, 20, 255},
};

Gfx N64Red_PolyList[] = {
   gsSPVertex(N64Red_VertList+0,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSPEndDisplayList(),
};

Vtx N64Green_VertList[] = {
   { -24, -43, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 13, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 48, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { -44, -24, -14, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, -14, 0, 0, 0, 46, 143, 3, 255},
   { -44, -24, 42, 0, 0, 0, 47, 144, 7, 255},
   { -24, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { -44, -24, -14, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { -44, -24, 42, 0, 0, 0, 47, 144, 7, 255},
   { 23, -24, -12, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, 42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -42, 0, 0, 0, 46, 143, 3, 255},
   { 48, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 48, -24, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 48, -24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, -42, 0, 0, 0, 46, 143, 3, 255},
   { -44, -24, -14, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, 42, 0, 0, 0, 46, 143, 3, 255},
   { -44, 24, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, 42, 0, 0, 0, 46, 143, 3, 255},
   { -44, -24, -14, 0, 0, 0, 46, 143, 3, 255},
   { -24, -24, -14, 0, 0, 0, 46, 143, 3, 255},
   { -24, 43, -12, 0, 0, 0, 46, 143, 3, 255},
   { -44, 43, 42, 0, 0, 0, 48, 144, 12, 255},
   { -24, 43, 42, 0, 0, 0, 52, 145, 26, 255},
   { 23, 43, 13, 0, 0, 0, 46, 143, 3, 255},
   { 48, 43, -42, 0, 0, 0, 47, 143, 8, 255},
   { 23, 43, -42, 0, 0, 0, 48, 144, 13, 255},
   { 23, 43, 13, 0, 0, 0, 46, 143, 3, 255},
   { 48, 43, 42, 0, 0, 0, 46, 143, 3, 255},
   { 48, 43, -42, 0, 0, 0, 47, 143, 8, 255},
   { 23, 43, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, 43, 42, 0, 0, 0, 46, 143, 3, 255},
   { 48, 43, 42, 0, 0, 0, 46, 143, 3, 255},
   { 23, 43, 13, 0, 0, 0, 46, 143, 3, 255},
   { -24, 43, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, 43, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 43, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, 43, 13, 0, 0, 0, 46, 143, 3, 255},
   { -44, 43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 43, -12, 0, 0, 0, 46, 143, 3, 255},
   { -24, 43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -44, 43, 42, 0, 0, 0, 48, 144, 12, 255},
   { -24, 43, -12, 0, 0, 0, 46, 143, 3, 255},
   { -44, 43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 13, 0, 0, 0, 46, 143, 3, 255},
   { -44, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, 42, 0, 0, 0, 46, 143, 3, 255},
   { 48, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { 48, -43, 42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, -12, 0, 0, 0, 46, 143, 3, 255},
   { 48, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, 42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { 48, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, -12, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, -43, -12, 0, 0, 0, 46, 143, 3, 255},
   { -44, -43, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 13, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 42, 0, 0, 0, 46, 143, 3, 255},
   { -44, -43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, -43, 13, 0, 0, 0, 46, 143, 3, 255},
   { -44, -43, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { -44, 24, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -42, 0, 0, 0, 46, 143, 3, 255},
   { 48, 24, -12, 0, 0, 0, 55, 146, 31, 255},
   { 23, 24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 48, 24, 42, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 48, 24, -12, 0, 0, 0, 55, 146, 31, 255},
   { 23, 24, 42, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 48, 24, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, 42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, 13, 0, 0, 0, 46, 143, 3, 255},
   { -44, 24, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, 12, 0, 0, 0, 46, 143, 3, 255},
   { -44, 24, 12, 0, 0, 0, 46, 143, 3, 255},
   { -44, 24, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, 12, 0, 0, 0, 46, 143, 3, 255},
   { 23, 43, 13, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 43, -42, 0, 0, 0, 46, 143, 3, 255},
   { -24, 24, -42, 0, 0, 0, 46, 143, 3, 255},
   { 23, 43, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, 13, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, 42, 0, 0, 0, 46, 143, 3, 255},
   { 48, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 48, 24, -12, 0, 0, 0, 46, 143, 3, 255},
   { 23, -24, 42, 0, 0, 0, 46, 143, 3, 255},
   { 48, -24, 42, 0, 0, 0, 46, 143, 3, 255},
};

Gfx N64Green_PolyList[] = {
   gsSPVertex(N64Green_VertList+0,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPVertex(N64Green_VertList+30,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPVertex(N64Green_VertList+60,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPVertex(N64Green_VertList+90,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPEndDisplayList(),
};

Vtx N64Blue_VertList[] = {
   { -24, -43, 13, 0, 0, 0, 0, 5, 143, 255},
   { -24, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -24, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { -24, 43, 42, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { -24, 43, -12, 0, 0, 0, 0, 5, 143, 255},
   { -24, 43, 42, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, 42, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -24, 43, 42, 0, 0, 0, 0, 5, 143, 255},
   { -24, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, 42, 0, 0, 0, 0, 5, 143, 255},
   { -24, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -24, -22, -42, 0, 0, 0, 0, 5, 143, 255},
   { -24, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -24, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -24, -43, -42, 0, 0, 0, 0, 5, 143, 255},
   { -24, -22, -42, 0, 0, 0, 0, 5, 143, 255},
   { -24, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -24, -43, 13, 0, 0, 0, 0, 5, 143, 255},
   { -24, -43, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -44, 43, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -44, -43, 42, 0, 0, 0, 0, 5, 143, 255},
   { -44, -24, 42, 0, 0, 0, 0, 5, 143, 255},
   { -44, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -44, -43, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, -43, 42, 0, 0, 0, 0, 5, 143, 255},
   { -44, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -44, -22, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, -43, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -44, -22, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, 42, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -44, -24, -14, 0, 0, 0, 0, 5, 143, 255},
   { -44, 43, 42, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, 42, 0, 0, 0, 0, 5, 143, 255},
   { -44, 43, -42, 0, 0, 0, 0, 5, 143, 255},
   { -44, 24, 12, 0, 0, 0, 0, 5, 143, 255},
   { -44, 43, 42, 0, 0, 0, 0, 5, 143, 255},
   { 23, -43, -12, 0, 0, 0, 0, 5, 143, 255},
   { 23, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, -24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, -42, 0, 0, 0, 0, 5, 143, 255},
   { 23, 43, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, 43, -42, 0, 0, 0, 0, 5, 143, 255},
   { 23, 43, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, -42, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 23, 43, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, -42, 0, 0, 0, 0, 5, 143, 255},
   { 23, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, -24, 42, 0, 0, 0, 0, 5, 143, 255},
   { 23, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 23, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, -43, 42, 0, 0, 0, 5, 7, 143, 255},
   { 23, -24, 42, 0, 0, 0, 0, 5, 143, 255},
   { 23, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 23, -43, -12, 0, 0, 0, 0, 5, 143, 255},
   { 23, -43, 42, 0, 0, 0, 5, 7, 143, 255},
   { 48, -43, 42, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, 42, 0, 0, 0, 0, 5, 143, 255},
   { 48, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 48, 43, 42, 0, 0, 0, 0, 5, 143, 255},
   { 48, 24, 42, 0, 0, 0, 0, 5, 143, 255},
   { 48, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 48, 43, -42, 0, 0, 0, 3, 7, 143, 255},
   { 48, 43, 42, 0, 0, 0, 0, 5, 143, 255},
   { 48, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 48, 24, -42, 0, 0, 0, 0, 5, 143, 255},
   { 48, 43, -42, 0, 0, 0, 3, 7, 143, 255},
   { 48, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 48, 24, -42, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, 42, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 48, 24, -12, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 48, -43, -42, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, -42, 0, 0, 0, 0, 5, 143, 255},
   { 48, -24, 13, 0, 0, 0, 0, 5, 143, 255},
   { 48, -43, 42, 0, 0, 0, 0, 5, 143, 255},
   { 48, -43, -42, 0, 0, 0, 0, 5, 143, 255},
};

Gfx N64Blue_PolyList[] = {
   gsSPVertex(N64Blue_VertList+0,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPVertex(N64Blue_VertList+30,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPVertex(N64Blue_VertList+60,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSP1Triangle(6, 7, 8, 0),
   gsSP1Triangle(9, 10, 11, 0),
   gsSP1Triangle(12, 13, 14, 0),
   gsSP1Triangle(15, 16, 17, 0),
   gsSP1Triangle(18, 19, 20, 0),
   gsSP1Triangle(21, 22, 23, 0),
   gsSP1Triangle(24, 25, 26, 0),
   gsSP1Triangle(27, 28, 29, 0),
   gsSPVertex(N64Blue_VertList+90,30,0),
   gsSP1Triangle(0, 1, 2, 0),
   gsSP1Triangle(3, 4, 5, 0),
   gsSPEndDisplayList(),
};


text:
#include <nusys.h>
#include "graphic.h"

int graphicsTaskNum = 0;
GraphicsTask graphicsTasks[MAX_GRAPHICS_TASKS];

// Pointer to the end of the displaylist we are currently working on.
// A lot of the example code uses a global variable for this because otherwise
// you'd need to pass this around (by reference) a lot.
Gfx * displayListPtr;

GraphicsTask * gfxSwitchTask() {
  GraphicsTask * nextTask;
  // switch the current graphics task
  graphicsTaskNum = (graphicsTaskNum + 1) % MAX_GRAPHICS_TASKS;
  nextTask = &graphicsTasks[graphicsTaskNum];
  displayListPtr = &nextTask->displayList[0];
  return nextTask;
}

// The initialization of RSP/RDP
void gfxRCPInit(void)
{
  // set the RSP segment register
  gSPSegment(displayListPtr++, 0, 0x0);  // For the CPU virtual address

  // init the RSP
  gSPDisplayList(displayListPtr++, OS_K0_TO_PHYSICAL(setup_rspstate));

  // init the RDP
  gSPDisplayList(displayListPtr++, OS_K0_TO_PHYSICAL(setup_rdpstate));
}

// clears the frame buffer and z buffer
void gfxClearCfb(void)
{
  // Clear the Z-buffer
  gDPSetDepthImage(displayListPtr++, OS_K0_TO_PHYSICAL(nuGfxZBuffer));
  gDPSetCycleType(displayListPtr++, G_CYC_FILL);
  gDPSetColorImage(displayListPtr++, G_IM_FMT_RGBA, G_IM_SIZ_16b,SCREEN_WD,
		   OS_K0_TO_PHYSICAL(nuGfxZBuffer));
  gDPSetFillColor(displayListPtr++,(GPACK_ZDZ(G_MAXFBZ,0) << 16 |
			       GPACK_ZDZ(G_MAXFBZ,0)));
  gDPFillRectangle(displayListPtr++, 0, 0, SCREEN_WD-1, SCREEN_HT-1);
  gDPPipeSync(displayListPtr++);

  // Clear the frame buffer
  gDPSetColorImage(displayListPtr++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD,
		   osVirtualToPhysical(nuGfxCfb_ptr));
  gDPSetFillColor(displayListPtr++, (GPACK_RGBA5551(0, 0, 0, 1) << 16 |
				GPACK_RGBA5551(0, 0, 0, 1)));
  gDPFillRectangle(displayListPtr++, 0, 0, SCREEN_WD-1, SCREEN_HT-1);
  gDPPipeSync(displayListPtr++);
}


text:
#!/bin/bash
set -eu

# this script will build and deploy the rom to an Everdrive64 flashcart

MAKEMASK=1 ./build.sh

for f in *.n64; do
  romfile="$f"
done

function fail {
  echo $1 >&2
  exit 1
}

function retry {
  local n=1
  local max=5
  local delay=1
  while true; do
    "$@" && break || {
      if [[ $n -lt $max ]]; then
        ((n++))
        echo "Command failed. Attempt $n/$max:"
        sleep $delay;
      else
        fail "The command has failed after $n attempts."
      fi
    }
  done
}

# loader64 can be found here: https://github.com/jsdf/loader64

# upload rom
retry loader64 -v --write --file "$romfile"

sleep 1;
# boot rom that was uploaded
retry loader64 -v --pifboot


text:
#!/bin/bash
# /Applications/Games/mupen64plus-bundle-osx-2.5.9/mupen64plus.app/Contents/MacOS/mupen64plus  --corelib /Users/jfriend/code/mupen64plus-core/projects/unix/libmupen64plus.dylib  ./*.n64
/Applications/Games/mupen64plus-bundle-osx-2.5.9/mupen64plus.app/Contents/MacOS/mupen64plus   ./*.n64


text:
#include <nusys.h>
#include "graphic.h"

/*
  The viewport structure
  The conversion from (-1,-1,-1)-(1,1,1).  The decimal part is 2-bit.
 */
static Vp vp = {
    SCREEN_WD*2, SCREEN_HT*2, G_MAXZ/2, 0,	/* The scale factor  */
    SCREEN_WD*2, SCREEN_HT*2, G_MAXZ/2, 0,	/* Move  */
};

/*
  The initialization of RDP
*/
Gfx setup_rdpstate[] = {
  gsDPSetRenderMode(G_RM_OPA_SURF, G_RM_OPA_SURF2),
  gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE),
  gsDPSetScissor(G_SC_NON_INTERLACE, 0,0, SCREEN_WD,SCREEN_HT),
  gsDPSetColorDither(G_CD_BAYER),
  gsSPEndDisplayList(),
};

/*
  The initialization of RSP
*/
Gfx setup_rspstate[] = {
  gsSPViewport(&vp),
  gsSPClearGeometryMode(0xFFFFFFFF),
  gsSPSetGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH | G_CULL_BACK),
  gsSPTexture(0, 0, 0, 0, G_OFF),
  gsSPEndDisplayList(),
};


text:
#!/bin/bash
/Applications/Games/RetroArch.app/Contents/MacOS/RetroArch -L /Applications/Games/RetroArch.app/Contents/Resources/cores/parallel_n64_libretro.dylib ./*.n64


text:
/*
	ROM spec file

	Main memory map

  	0x80000000  exception vectors, ...
  	0x80000400  zbuffer (size 320*240*2)
  	0x80025c00  codesegment
	      :
  	0x8038F800  cfb 16b 3buffer (size 320*240*2*3)

*/

#include <nusys.h>

/* Use all graphic microcodes */
beginseg
	name	"code"
	flags	BOOT OBJECT
	entry 	nuBoot
	address NU_SPEC_BOOT_ADDR
        stack   NU_SPEC_BOOT_STACK
	include "codesegment.o"
	include "$(ROOT)/usr/lib/PR/rspboot.o"
	include "$(ROOT)/usr/lib/PR/gspF3DEX2.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspL3DEX2.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspF3DEX2.Rej.fifo.o"
        include "$(ROOT)/usr/lib/PR/gspF3DEX2.NoN.fifo.o"
        include "$(ROOT)/usr/lib/PR/gspF3DLX2.Rej.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspS2DEX2.fifo.o"
endseg

beginwave
	name	"squaresdemo"
	include	"code"
endwave


text:
#  squaresdemo Makefile
#  this is just the standard nusystem makefile

include $(ROOT)/usr/include/make/PRdefs

N64KITDIR    = c:\nintendo\n64kit
NUSYSINCDIR  = $(N64KITDIR)/nusys/include
NUSYSLIBDIR  = $(N64KITDIR)/nusys/lib

LIB = $(ROOT)/usr/lib
LPR = $(LIB)/PR
INC = $(ROOT)/usr/include
CC  = gcc
LD  = ld
MAKEROM = mild

LCDEFS =	-DNU_DEBUG -DF3DEX_GBI_2
LCINCS =	-I. -I$(NUSYSINCDIR) -I$(ROOT)/usr/include/PR
LCOPTS =	-G 0
LDFLAGS = $(MKDEPOPT) -L$(LIB) -L$(NUSYSLIBDIR) -lnusys_d -lgultra_d -L$(GCCDIR)/mipse/lib -lkmc

OPTIMIZER =	-g

APP =		squaresdemo.out

TARGETS =	squaresdemo.n64

HFILES =	graphic.h

CODEFILES   = 	main.c stage00.c graphic.c gfxinit.c

CODEOBJECTS =	$(CODEFILES:.c=.o)  $(NUSYSLIBDIR)/nusys.o

DATAFILES   =

DATAOBJECTS =	$(DATAFILES:.c=.o)

CODESEGMENT =	codesegment.o

OBJECTS =	$(CODESEGMENT) $(DATAOBJECTS)

default:        $(TARGETS)

include $(COMMONRULES)

$(CODESEGMENT):	$(CODEOBJECTS) Makefile
		$(LD) -o $(CODESEGMENT) -r $(CODEOBJECTS) $(LDFLAGS)

$(TARGETS):	$(OBJECTS)
		$(MAKEROM) spec -I$(NUSYSINCDIR) -r $(TARGETS) -e $(APP)
ifdef MAKEMASK
		makemask $(TARGETS)
endif


text:
#ifndef _STAGE00_H_
#define _STAGE00_H_

#include "graphic.h"

void stage00(int);
void initStage00();

void drawSquare();
void drawN64Logo();

#endif /* _STAGE00_H_ */


text:
#ifndef _GRAPHIC_H_
#define _GRAPHIC_H_

// The screen size
#define SCREEN_HT        240
#define SCREEN_WD        320

#define MAX_GRAPHICS_TASKS 2
// The maximum length of the display list of one task
#define MAX_DISPLAY_LIST_COMMANDS 2048
// determines the number of matrices we allocate, because we need one for every
// object we want to position in the world, for each graphics task
#define MAX_OBJECTS 10

#define FOVY 45
#define ASPECT (f32)SCREEN_WD/(f32)SCREEN_HT
#define NEAR_PLANE 10
#define FAR_PLANE 1000

// a 3d position, such as that of an object
typedef struct Vec3d {
  float x;
  float y;
  float z;
} Vec3d;

// a struct to hold graphics data used by the RCP which can change at runtime
typedef struct GraphicsTask {
  Mtx projection;
  Mtx modelview;
  Mtx objectTransforms[MAX_OBJECTS];
  Gfx displayList[MAX_DISPLAY_LIST_COMMANDS];
} GraphicsTask;

extern struct GraphicsTask graphicsTasks[MAX_GRAPHICS_TASKS];
extern Gfx * displayListPtr;

extern GraphicsTask * gfxSwitchTask();
extern void gfxRCPInit();
extern void gfxClearCfb();

extern Gfx setup_rdpstate[];
extern Gfx setup_rspstate[];

#endif /* _GRAPHIC_H_ */


text:
#ifndef _MAIN_H_
#define _MAIN_H_

extern NUContData contdata[1]; // input data for one controller

#endif /* _MAIN_H_ */


text:
#!/bin/bash
set -eu

WINE_PATH="/Applications/Wine Stable.app/Contents/Resources/wine/bin/wine"

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
rm -f *.o
"$WINE_PATH" cmd /c $DIR/compile.bat


text:
#include <nusys.h>
#include "stage00.h"

NUContData  contdata[1]; // storage for controller 1 inputs

void mainproc(void)
{
  // initialize the graphics system
  nuGfxInit();

  // initialize the controller manager
  nuContInit();

  // initialize the level
  initStage00();

  // set the update+draw callback to be called every frame
  nuGfxFuncSet((NUGfxFunc)stage00);

  // enable video output
  nuGfxDisplayOn();

  // send this thread into an infinite loop
  while(1)
    ;
}


text:
set startpath=%cd%
set startdrive=%CD:~0,2%

C:
cd C:\ultra
call setup.bat

%startdrive%
cd %startpath%
exew32 make


text:
# Nintendo 64 homebrew demo & tutorial

This is a demo app with heavily-commented source showing basic usage of the N64 SDK and the NuSystem framework. The game-specific code is in [stage00.c](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c).

The compiled rom file can be found [here](https://github.com/jsdf/n64-sdk-demo/raw/master/squaresdemo.n64.zip). It can be run with an emulator or an N64 flashcart. Try pressing the A and B buttons.

![recording of the demo](https://media.giphy.com/media/hTmlPHZq8LSDnyGRH7/giphy.gif)

## How to build (macOS or linux):

- Install [wine](https://www.winehq.org/). NOTE: wine currently doesn't work with macOS versions [10.15 and up](https://wiki.winehq.org/MacOS)
- Install the n64 sdk. this repo assumes it's in the default location of `C:\ultra` (in the wine filesystem). If you've installed it somewhere else, you'll need to update this path in `compile.bat`
- Edit `./build.sh` so that WINE_PATH points to your wine binary
- Run `./build.sh`. This should build squaresdemo.n64
- Run squaresdemo.n64 with an emulator or an N64 flashcart

## Understanding the code (or, an N64 homebrew tutorial)

There are basically two options for making an N64 game these days:
- the official Nintendo SDK from the 90s, which comes in Windows 95 and SGI IRIX flavours.
- the modern open source toolchain, which centers on the [libdragon](https://github.com/DragonMinded/libdragon) library and tools.

This tutorial uses the SDK. RetroReversing has a [pretty good tutorial for installing and using the SDK](https://www.retroreversing.com/n64-sdk-setup) under Wine, which will allow you to compile the code in this repo.

The N64 SDK comes with a small framework for quickly starting a new game, called NuSystem. The N64 comes with an OS (really, a library that you link into your game binary and boot on the bare metal), which provides features like threads and I/O, but still requires a fair bit of boilerplate to get a game engine set up. NuSystem removes the need to think about threads and initializing the hardware, and just lets you provide the typical `setup()`, `update()`, and `draw()` callbacks that form the core of many simple game engines. In NuSystem these functions are typically called [`initStage00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L41), [`updateGame00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L60), and [`makeDL00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L91) respectively, where `00` is the stage/level number of the game.

### Initialization
When a game using NuSystem boots, it runs the `mainproc()` function in [main.c](https://github.com/jsdf/n64-sdk-demo/blob/master/main.c#L8). This then calls [`initStage00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L41) to initialize the level, and then [registers the `stage00` callback](https://github.com/jsdf/n64-sdk-demo/blob/master/main.c#L20) so that [`updateGame00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L60), and [`makeDL00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L91) can be called on each frame.

From there it's up to you to fill in the logic for these functions with your game-specific code.

### Reading controller input and updating the game world

In [`initStage00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L41), we initialized some state: the `squaresRotationDirection` boolean, and the `squaresRotations` array.

In [`updateGame00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L60) we first read the controller input, then update these values as appropriate, each frame. In this example we rotate all the squares a bit each frame, and if the A button is pressed, reverse the direction of their rotation. We'll use these values to determine what to render in the [`makeDL00()`](https://github.com/jsdf/n64-sdk-demo/blob/master/stage00.c#L91) function.

### Rendering

On the N64, at a high level the procedure for rendering graphics looks something like this:

- start a new a 'display list' (a list of commands to draw stuff on screen, that is stored in memory)
- calculate projection and modelview matrices based on camera position and properties (type of projection, field of view, aspect ratio)
- build up the display list by iterating through world objects and adding commands to transform the current drawing position/rotation/scale using transformation matrices, and then draw model meshes, potentially with some texture and lighting settings
- send the display list off as a 'graphics task' to the RCP ('Reality Co-Processor', basically the GPU of the N64) to be rendered

Rendering on the N64 is a cooperative act between the main CPU, which runs the game logic and produces the list of things to draw, and the RCP, which performs the drawing of one frame while the main CPU moves on to running the logic and producing the display list for the next frame. To do this, we will need to allocate some structures to hold the displaylists and other data that will be shared between the code which runs on the main CPU (our program) and the code which runs on the RCP (called 'microcode', provided in binary form as part of the SDK). We need to allocate separate instances of this shared data for each frame, and switch which instance we're using when starting a new frame, like a circular buffer. The `gfxTaskSwitch()` function in [graphic.c](https://github.com/jsdf/n64-sdk-demo/blob/master/graphic.c#L14-L22) shows one implementation of this. For a more detailed picture of the relationship between the CPU and the RCP, see [this diagram](https://github.com/jsdf/n64-sdk-demo/raw/master/n64%20cpu%20rcp.png).

#### The Matrix Stack

Another concept that's worth understanding is the 'matrix stack'. This concept comes directly from OpenGL 1.0 (which makes sense as both the Nintendo 64 hardware and OpenGL were developed by SGI). If you're put off by matrix math, don't worry, you don't need to understand it to make use of this feature. If you've used 2D drawing APIs like Turtle graphics or the Canvas2DContext in the HTML Canvas API, you'll be familiar with moving the drawing position by applying successive relative transformations such as translation, rotation and scaling, and this is very similar.

The matrix stack is a stack data structure of matrices representing transformations of current drawing position in 3D space, which can be pushed onto and popped off. Pushing a matrix onto the stack effectively means applying a relative transformation to the drawing position which we can later undo by popping it back off. This allows rendering of hierarchical structures which might have transformations relative to some parent object, such as the positions and rotations of the wheels of a car relative to the car, and the car itself positioned and rotated in relation to the world. Modern OpenGL no longer includes this concept in its API, but for the N64 being able to offload the work of performing the matrix math (to transform objects in 3D space) to the RCP necessitated this sort of API.

In OpenGL 1.0 this looks something like:

```c

glMatrixMode(GL_MODELVIEW); // using modelview matrix stack
glLoadIdentity(); // start at 0,0,0, with no rotation or scaling
glPushMatrix();
  glTranslate(10, 0, 0); // translate in the x axis
  drawCube(); // draw something, translated
  glPushMatrix();
    glRotate(0, 45, 0); // rotate around the y axis
    glScale(1, 2, 1); // scale in the y axis
    drawPyramid(); // draw something else, translated, rotated and scaled
  glPopMatrix(); // undo rotation and scale
glPopMatrix(); // undo translation
drawSphere(); // draw something else, back at 0,0,0
```

On the N64, the equivalent code is:

```c
int curMatrix = 0;

// start at 0,0,0, with no rotation or scaling
guMtxIdent(&gfxTask->objectTransforms[curMatrix]);
gSPMatrix(displayListPtr++,
  OS_K0_TO_PHYSICAL(&(gfxTask->objectTransforms[curMatrix++])),
  G_MTX_MODELVIEW | G_MTX_NOPUSH | G_MTX_LOAD
);
// translate in the x axis
guTranslate(&gfxTask->objectTransforms[curMatrix], 10, 0, 0);
// push translation matrix
gSPMatrix(displayListPtr++,
  OS_K0_TO_PHYSICAL(&(gfxTask->objectTransforms[curMatrix++])),
  G_MTX_MODELVIEW | G_MTX_PUSH | G_MTX_MUL
);

drawCube(); // draw something, translated

// rotate around the y axis
guRotate(&gfxTask->objectTransforms[curMatrix], 0, 45, 0);
// scale in the y axis
guScale(&gfxTask->objectTransforms[curMatrix], 1, 2, 1);

// push transformation matrix
gSPMatrix(displayListPtr++,
  OS_K0_TO_PHYSICAL(&(gfxTask->objectTransforms[curMatrix++])),
  G_MTX_MODELVIEW | G_MTX_PUSH | G_MTX_MUL
);

drawPyramid(); // draw something else, translated, rotated and scaled

// undo rotation and scale
gSPPopMatrix(displayListPtr++, G_MTX_MODELVIEW);
// undo translation
gSPPopMatrix(displayListPtr++, G_MTX_MODELVIEW);

drawSphere(); // draw something else, back at 0,0,0
```

Although there is a strong correspondence between the two APIs, there are some important differences. On N64, you are responsible for allocating and the matrix (`Mtx`) data structures, whereas OpenGL hides them inside its implementation. Additionally, when you use a function like `guTranslate()` or  `guRotate()`, it immediately mutates the matrix you passed in. However, rendering happens later (on the RCP), so any drawing commands between the surrounding `gSPMatrix()` and `gSPPopMatrix()` commands will be rendered with the same, final matrix value (eg. the same transform).

#### Rendering squares

The rendering of each square is wrapped up in the `drawSquare()` function. The square is defined in `squareVerts`, and it is made up of 2 triangles, and colored by RGB colors defined for each vertex.
For each of the items in the `squares` array, we push a transformation matrix onto the matrix stack with the position and current rotation of the square, then add a bunch of displaylist commands to load the triangle vertices forming the square, and set a bunch of RCP settings affecting the appearance of the square.  Finally we render the square with the `gSP2Triangles()` command. We also add a `gDPPipeSync()` command to mark the end of the current primative (rendered object sharing the same settings), and use pop our transformation back off the stack with `gSPPopMatrix()`.

#### Rendering a complex mesh

A common approach to including models in your game created in a 3d modeling is to convert them into C source files containing static display lists. A static displaylist is a list of displaylist commands defined in an array. You can find an example of this in the `drawN64Logo()` function, which uses the `gSPDisplayList()` command to render the static display lists defined in [n64logo.h](https://github.com/jsdf/n64-sdk-demo/blob/master/n64logo.h#L1).

### Beyond...

As the SDK docs are online, you can google any N64 function name to find documentation about it, or you can use the google 'site search' feature to search for some topic, for example: [site:http://n64devkit.square7.ch/ memory management](https://www.google.com/search?q=site%3Ahttp%3A%2F%2Fn64devkit.square7.ch%2F+memory+management).

If you would like to see an example of a more complete game codebase, take a look at [goose64](https://github.com/jsdf/goose64).


text:
# Blen64 (modified)
# forked from https://github.com/GCaldL/Blen64
# this script uses the vertex buffer inefficiently, which can result in poor
# rendering performance. keep that in mind if you use it.
# - fixed invalid output of x,y,z
# - fixed invalid use of vertex buffer indices > 32
# - fixed random value used for flag
# - fixed random value used for alpha
# - removed support for UV coordinates

import bpy
import string

s = 100  # scale constant
loadlim = 30  # amount of verts the sytem will load at a time 32 max limit

bpy.ops.text.new()
o = bpy.data.texts["Text"]
o.name = "meshout"

def clean_name(name):
    # Only take the characters from the name that are valid for C identifiers.
    # Note that this makes it possible to have a duplicate, so we test against all
    # previous names before using.

    name = "".join(
        c
        for c in obj.name
        if c in string.ascii_letters or c in string.digits or c == "_"
    )
    if len(name) == 0 or name[0] in string.digits:
        name = "_" + name
    return name

def export(obj):
    name = clean_name(obj.name)
    vert = obj.data.vertices
    poly = obj.data.polygons

    ###Triangulate
    # Check for polys w/ more than 3 verts
    for p in poly:
        if len(p.vertices) > 3:
            bpy.ops.object.editmode_toggle()
            bpy.ops.mesh.quads_convert_to_tris(
                quad_method="BEAUTY", ngon_method="BEAUTY"
            )
            bpy.ops.object.editmode_toggle()
            break

    # Vertex List
    o.write("Vtx %s_VertList[] = {\n" % name)
    for face in poly:
        for vert, loop in zip(face.vertices, face.loop_indices):
            coord = obj.data.vertices[vert].co
            vcol = (
                obj.data.vertex_colors.active.data[loop].color
                if obj.data.vertex_colors.active
                else (1, 1, 1, 1)
            )
            o.write(
                "   { %i, %i, %i, %i, %i, %i, %i, %i, %i, %i},\n"
                % (
                    coord.x * s,
                    coord.y * s,
                    coord.z * s,
                    0,  # flag
                    0,  # S
                    0,  # T
                    vcol[0] * 255,
                    vcol[1] * 255,
                    vcol[2] * 255,
                    255,  # alpha
                )
            )
    o.write("};\n\n")

    # Face List
    o.write("Gfx %s_PolyList[] = {\n" % name)
    o.write(
        "   gsSPVertex(%s_VertList+%d,%d,0),\n" % (name, 0, loadlim)
    )  # load the first x number of vertices
    i = 0
    offset = 0
    for face in poly:
        # if the face being created contains a vert that has not been loaded into the buffer
        if i * 3 + 2 > offset + loadlim:
            offset = i * 3
            o.write(
                "   gsSPVertex(%s_VertList+%i,%i,%i),\n" % (name, offset, loadlim, 0)
            )

        # build faces:
        o.write(
            "   gsSP1Triangle(%d, %d, %d, %d),\n"
            % (i * 3 - offset, i * 3 + 1 - offset, i * 3 + 2 - offset, 0)
        )
        i += 1
    o.write("   gsSPEndDisplayList(),\n")
    o.write("};\n\n")

for obj in bpy.context.selected_objects:
    export(obj)


text:
#!/usr/bin/env bash

#some setup stuff for the dev environment for Github Copilot CLI
#https://www.npmjs.com/package/@githubnext/github-copilot-cli
#install nodejs
curl -fsSL https://deb.nodesource.com/setup_19.x | sudo -E bash - &&\
sudo apt-get install -y nodejs

#install GitHub Copilot CLI
npm install -g @githubnext/github-copilot-cli

#authenticate with GitHub Copilot
github-copilot-cli auth

#Upgrade
#npm install -g @githubnext/github-copilot-cli

repo_name: nogibjj/rusty-deploy

repo_language: Makefile

mime_type: text/plain


text:
#!/usr/bin/env bash
## Format all code directories in the repostitory using cargo fmt.

for DIR in */; do
    DIRNAME=$(basename "$DIR")
    echo "==> $DIRNAME <=="
    (cd $DIR && cargo clippy --all-targets --all-features -- -D warnings)
done

echo "Format complete."


text:
install:
	# Install if needed
	#@echo "Updating rust toolchain"
	#rustup update stable
	#rustup default stable

rust-version:
	@echo "Rust command-line utility versions:"
	rustc --version 			#rust compiler
	cargo --version 			#rust package manager
	rustfmt --version			#rust code formatter
	rustup --version			#rust toolchain manager
	clippy-driver --version		#rust linter

format:
	@echo "Formatting all projects with cargo"
	./format.sh

lint:
	@echo "Linting all projects with cargo"
	@rustup component add clippy 2> /dev/null
	./lint.sh

test:
	@echo "Testing all projects with cargo"
	./test.sh

check-gpu-linux:
	sudo lshw -C display

all: format lint test


text:
#!/usr/bin/env bash
## Format all code directories in the repostitory using cargo fmt.

for DIR in */; do
    DIRNAME=$(basename "$DIR")
    echo "==> $DIRNAME <=="
    (cd $DIR && cargo test)
done

echo "Format complete."


text:
#!/usr/bin/env bash
## Format all code directories in the repostitory using cargo fmt.

for DIR in */; do
    DIRNAME=$(basename "$DIR")
    echo "==> $DIRNAME <=="
    (cd $DIR && cargo fmt -- )
done

echo "Format complete."


text:
## Rust Deployment Experiments

My hypothesis is RUST is the ideal language for LLMOPs, NOT PYTHON.

### Workflows to Prove Out for Rust MLOps Deployment

#### Distroless

###### Rust T5 Model

##### PyTorch Distroless Pre-Trained Model

![11 10-pytorch-pretrained-models](https://user-images.githubusercontent.com/58792/233207475-c97818ec-5ab2-4e44-947e-dc9296e47ae7.png)

* [Package PyTorch into a Rust Distroless Container](https://github.com/nogibjj/rusty-deploy/tree/main/rtorchdist)
![Screenshot 2023-04-03 at 2 09 38 PM](https://user-images.githubusercontent.com/58792/229592006-9a0c59c1-e1d0-43c7-bc97-dda3d891ec91.png)

##### Hugging Face Distroless Pre-Trained Model

#### Binary Deployment

* Statically Linked ONNX to Rust Binary
* Statically Linked PyTorch to Rust Binary
* Statically Linked HuggingFace to Rust Binary

### References

* [huggingface/text-generation-inference a Rust example](https://github.com/huggingface/text-generation-inference)


text:
# These are supported funding model platforms

github: noahgift


text:
version: 2
updates:
- package-ecosystem: cargo
  directory: "/"
  schedule:
    interval: daily
- package-ecosystem: github-actions
  directory: "/"
  schedule:
    interval: daily


text:
name: Rust CI/CD Pipeline
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
env:
  CARGO_TERM_COLOR: always
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions-rs/toolchain@v1
      with:
          toolchain: stable
          profile: minimal
          components: clippy, rustfmt
          override: true
    - name: update linux
      run: sudo apt update
    - name: update Rust
      run: make install
    - name: Check Rust versions
      run: make rust-version
    - name: Format
      run: make format
    - name: Lint
      run: make lint
    - name: Test
      run: make test


text:
version: '3.8'

volumes:
  postgres-data:

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    env_file:
        # Ensure that the variables in .env match the same variables in devcontainer.json
        - .env

    volumes:
      - ../..:/workspaces:cached

    # Overrides default command so things don't shut down after the process ends.
    command: sleep infinity

    # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
    network_mode: service:db

    # Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
    # (Adding the "ports" property to this file will not forward from a Codespace.)

  db:
    image: postgres:14.1
    restart: unless-stopped
    volumes:
      - postgres-data:/var/lib/postgresql/data
    env_file:
        # Ensure that the variables in .env match the same variables in devcontainer.json
        - .env

    # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
    # (Adding the "ports" property to this file will not forward from a Codespace.)


text:
FROM mcr.microsoft.com/devcontainers/rust:0-1-bullseye

# Include lld linker to improve build times either by using environment variable
# RUSTFLAGS="-C link-arg=-fuse-ld=lld" or with Cargo's configuration file (i.e see .cargo/config.toml).
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
   && apt-get -y install clang lld lshw python3-venv python3-dev libssl-dev \
   && apt-get autoremove -y && apt-get clean -y


text:
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/rust-postgres
{
	"name": "Rust and PostgreSQL",
	"dockerComposeFile": "docker-compose.yml",
	"service": "app",
	"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
	"features": {
		"ghcr.io/devcontainers/features/docker-in-docker:2": {},
		"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {},
		"ghcr.io/devcontainers/features/nvidia-cuda:1": {
			"installCudnn": true,
		},
		"ghcr.io/devcontainers/features/rust:1": {},
		"ghcr.io/eitsupi/devcontainer-features/mdbook:0": {},
		"ghcr.io/guiyomh/features/vim:0": {}
	},

	// Features to add to the dev container. More info: https://containers.dev/features.
	// "features": {},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [5432],

	// Use 'postCreateCommand' to run commands after the container is created.
	"postCreateCommand": "rustc --version",
	"customizations": {
		"vscode": {
			"extensions": [
				"GitHub.copilot-labs",
				"GitHub.copilot-nightly",
				"ms-azuretools.vscode-docker",
				"technosophos.vscode-make",
				"ms-vscode.makefile-tools"
			]
		}
	},

	// Configure tool-specific properties.
	// "customizations": {},

	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root"
}


text:
#include <torch/torch.h>
#include <iostream>

int main() {
    torch::Tensor tensor = torch::rand({2, 3});
    std::cout << tensor << std::endl;
    return 0;
}


text:
#!/bin/bash

curl -X POST \
     -H "Content-Type: multipart/form-data" \
     -F "image=@./lion.jpg" \
     http://127.0.0.1:8080/predict


text:
import torch
from torchvision.models import resnet34
from torch.utils.mobile_optimizer import optimize_for_mobile
from torchvision.models.resnet import ResNet, ResNet34_Weights, BasicBlock

model_pt = resnet34(weights=ResNet34_Weights.IMAGENET1K_V1)
model_pt.eval()
scripted_model = torch.jit.script(model_pt)
optimized_model = optimize_for_mobile(scripted_model)
optimized_model.save("model/resnet34.ot")


text:
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(check_pytorch CXX)

find_package(Torch REQUIRED)

add_executable(check_pytorch check_pytorch.cpp)
target_link_libraries(check_pytorch "${TORCH_LIBRARIES}")
set_property(TARGET check_pytorch PROPERTY CXX_STANDARD 14)


text:
fn main() {
    println!("cargo:rustc-link-search=native=/path/to/torch/library");
    println!("cargo:rustc-link-lib=torch");
    println!("cargo:rustc-link-lib=dylib=torch");
}


text:
import torch
from torchvision import transforms
from PIL import Image

# Load the model
model = torch.jit.load("model/resnet34.ot")

# Load and preprocess the image
image = Image.open("lion.jpg")
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
])
image = transform(image)
image = image.unsqueeze(0)  # Add batch dimension

# Debug information
print("Input tensor shape:", image.shape)
print("Input tensor dtype:", image.dtype)

# Get input tensor as a numpy array
input_np = image.numpy()
print("Input tensor as array:")
print(input_np)

# Make a prediction
with torch.no_grad():
    output = model(image)
    probabilities = torch.nn.functional.softmax(output[0], dim=0)
    _, predicted_class = torch.max(probabilities, 0)

# Debug information
print("Output tensor shape:", output.shape)
print("Output tensor dtype:", output.dtype)

# Print the predicted class
print("Predicted class:", predicted_class.item())


text:
format:
	cargo fmt --quiet

lint:
	cargo clippy --quiet

refactor: format lint

build:
	cargo build -j 16 --release

dbuild:
	docker build -t pytorch-rust-distroless .

drun:
	docker run -p 8080:8080 -e RUST_LOG="info,actix_web=info" -e RUST_BACKTRACE=1 pytorch-rust-distroless

test:
	RUST_BACKTRACE=1 RUST_LOG=debug,info cargo test -- --nocapture

make run:
	RUST_LOG="debug,info,trace,actix_web=info"; \
	RUST_BACKTRACE=1; \
	cargo run

all: format lint test run


text:
#!/bin/bash
# Description: install pytorch in linux

#Install cmake
sudo apt-get update && sudo apt-get install -y cmake
# Set Torch_DIR and CMAKE_PREFIX_PATH
export Torch_DIR=/usr/local/lib/libtorch/share/cmake/Torch
export CMAKE_PREFIX_PATH=$Torch_DIR:$CMAKE_PREFIX_PATH

# Source ~/.bashrc to ensure the environment variable is available in the current session
source ~/.bashrc

# Installs PyTorch in Linux
curl -LO https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-2.0.0%2Bcpu.zip
mv libtorch-cxx11-abi-shared-with-deps-2.0.0%2Bcpu.zip libtorch-cxx11-abi-shared-with-deps-2.0.0+cpu.zip
unzip libtorch-cxx11-abi-shared-with-deps-2.0.0+cpu.zip
rm libtorch-cxx11-abi-shared-with-deps-2.0.0+cpu.zip

# Move libtorch to /usr/local/lib
if [ -d "/usr/local/lib/libtorch" ]; then
  echo "/usr/local/lib/libtorch already exists. Deleting it to proceed with the installation."
  sudo rm -rf /usr/local/lib/libtorch
fi
sudo mv libtorch /usr/local/lib

# Export LIBTORCH and LD_LIBRARY_PATH
export LIBTORCH=/usr/local/lib/libtorch
export LD_LIBRARY_PATH=${LIBTORCH}/lib:$LD_LIBRARY_PATH

# Check if ~/.bashrc has the following lines
# export LIBTORCH=/path/to/libtorch
# export LD_LIBRARY_PATH=${LIBTORCH}/lib:$LD_LIBRARY_PATH
if grep -q "export LIBTORCH" ~/.bashrc; then
  echo "LIBTORCH is already in ~/.bashrc"
else
  echo "export LIBTORCH=/usr/local/lib/libtorch" >> ~/.bashrc
fi
if grep -q "export LD_LIBRARY_PATH" ~/.bashrc; then
  echo "LD_LIBRARY_PATH is already in ~/.bashrc"
else
  echo 'export LD_LIBRARY_PATH=${LIBTORCH}/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
fi

# Source the updated .bashrc
source ~/.bashrc

echo "Checking if libtorch is installed correctly..."
echo "Verifying that C++ PyTorch API is functional..."

cat > check_pytorch.cpp << EOL
#include <torch/torch.h>
#include <iostream>

int main() {
    torch::Tensor tensor = torch::rand({2, 3});
    std::cout << tensor << std::endl;
    return 0;
}
EOL

cmake -B build
cmake --build build --config Release
./build/check_pytorch

if [ $? -eq 0 ]; then
  echo "C++ PyTorch API is functional. Installation complete."
else
  echo "C++ PyTorch API is not functional. Please check the configuration and try again."
fi


text:
[package]
name = "rtorchdist"
version = "0.1.0"
edition = "2018"

[dependencies]
tch = "0.11.0"
actix-web = "4.0.0-beta.11"
actix-multipart = "0.6.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
image = "0.24.6"
env_logger = "0.10.0"
futures = "0.3.28"
imageproc = "0.23.0"
mime = "0.3.16"
failure = "0.1.8"
log = "0.4.14"
base64 = "0.21"
anyhow = "1.0"
test-log = "0.2.11"
actix-rt = "2.4.0"
headers = "0.3.4"
md5 = "0.7.0"

[profile.release]
opt-level = 3
codegen-units = 1
lto = "fat"
panic = "abort"


text:
numpy==1.24.2
Pillow==9.4.0
torchvision==0.15.1


text:
# First, we need a Rust build environment with Libtorch (PyTorch C++ backend) installed
FROM rust:1.67.1 as build-env
WORKDIR /app

# Install Libtorch (PyTorch C++ backend) v2.0.0
RUN curl -LO https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-2.0.0%2Bcpu.zip && \
    mv libtorch-cxx11-abi-shared-with-deps-2.0.0%2Bcpu.zip libtorch-cxx11-abi-shared-with-deps-2.0.0+cpu.zip && \
    unzip libtorch-cxx11-abi-shared-with-deps-2.0.0+cpu.zip && \
    rm libtorch-cxx11-abi-shared-with-deps-2.0.0+cpu.zip

# Set LIBTORCH environment variable
ENV LIBTORCH=/app/libtorch

# Copy the Rust code, the pre-trained PyTorch model, and the resnet34.ot file
COPY . /app

# Build the Rust Actix-web application with LIBTORCH set
ENV MODEL_PATH="/model/resnet34.ot"
RUN LIBTORCH=${LIBTORCH} cargo build --release

# Use a distroless cc image
FROM gcr.io/distroless/cc
COPY --from=build-env /app/target/release/rtorchdist /
COPY --from=build-env /app/model /model
COPY --from=build-env /app/libtorch /app/libtorch
COPY --from=build-env /app/model/resnet34.ot /resnet34.ot
COPY --from=build-env /app/tests/fixtures/lion.jpg /tests/fixtures/lion.jpg
ENV IMAGE_PATH /tests/fixtures/lion.jpg

# Set the required environment variables for Libtorch
ENV LD_LIBRARY_PATH="/app/libtorch/lib:${LD_LIBRARY_PATH}"
# Your container will fail if you haven't downloaded the model and set the path
ENV MODEL_PATH="/model/resnet34.ot"
# Start the Actix-web server
CMD ["./rtorchdist"]


text:
#!/bin/bash

echo "Testing route: /"
curl -X GET http://127.0.0.1:8080/

echo "Testing route: /check_image_prediction"
curl -X GET http://127.0.0.1:8080/check_image_prediction

echo "Testing route: /check_pytorch_cpu"
curl -X GET http://127.0.0.1:8080/check_pytorch_cpu

echo "Testing route: /check_image_upload"
curl -X POST -F "file=@tests/fixtures/lion.jpg" http://127.0.0.1:8080/check_image_upload

echo "Testing route: /predict"
curl -X POST -F "file=@tests/fixtures/lion.jpg" http://127.0.0.1:8080/predict

#echo "Testing route: /predict with external image"
wget "https://upload.wikimedia.org/wikipedia/commons/6/66/An_up-close_picture_of_a_curious_male_domestic_shorthair_tabby_cat.jpg" -O cat.jpg
curl -X POST -H "Content-Type: multipart/form-data" -F "image=@cat.jpg" http://127.0.0.1:8080/predict
rm cat.jpg


text:
## A Distroless Rust PyTorch Example

To run:

```
dbuild:
	docker build -t pytorch-rust-distroless .

drun:
	docker run -p 8080:8080 pytorch-rust-distroless
```

## Disclaimer!

This is built for POC (Proof of concept) and doesn't handle edge cases in any manner, you must extend.

### Rust Image Prediction Web Server

This is a Rust code that uses the Actix Web framework to create a simple web server that takes an image as input, runs it through a pre-trained ResNet34 model to make a prediction, and returns the predicted class and confidence score as a JSON response.

## Route: `/`

This is the root route that provides instructions on how to send an image payload to the server using curl. The payload must be sent as a `multipart/form-data` and the image file must be attached using the `-F` option in the curl command.

**Method:** `GET`

**Response:**

A `200 OK` response with a message containing instructions on how to send an image payload using curl.

## Route: `/predict`

This route is used to predict the content of an image. The image must be sent as a `multipart/form-data` payload and must be saved to a temporary directory before being passed to the prediction function. The predicted content of the image is returned in the response.

**Method:** `POST`

**Request Payload:**

A `multipart/form-data` payload containing an image file.

**Response:**

- If the prediction is successful, a `200 OK` response with a JSON object containing a `"status"` field with a value of `"success"` and a `"result"` field with the predicted content of the image.
- If the prediction fails, a `500 Internal Server Error` response with a JSON object containing a `"status"` field with a value of `"error"` and a `"message"` field with the error message.

## Route: `/check_image_upload`

This route is used to check if an image upload was successful. The image must be sent as a `multipart/form-data` payload and must be saved to a temporary directory before being passed to the check function. The response contains a JSON object with the status of the upload and the filepath of the saved image.

**Method:** `POST`

**Request Payload:**

A `multipart/form-data` payload containing an image file.

**Response:**

A `200 OK` response with a JSON object containing either a `"status"` field with a value of `"success"` and a `"filepath"` field with the path of the saved image or a `"status"` field with a value of `"error"` and an `"error"` field with the error message.

## Route: `/check_pytorch_cpu`

This route is used to check if PyTorch is running on the CPU. The response contains a message indicating whether the self-check was successful or not.

**Method:** `GET`

**Response:**

A `200 OK` response with a message indicating whether the self-check was successful or not.

## Route: `/check_image_prediction`

This route is used to check if the image recognition model is working correctly. The response contains the result of a self-check performed on the image recognition model.

**Method:** `GET`

**Response:**

- If the self-check is successful, a `200 OK` response with a JSON object containing a `"status"` field with a value of `"success"` and a `"result"` field with the self-check result.
- If the self-check fails, a `500 Internal Server Error` response with a JSON object containing a `"status"` field with a value of `"error"` and a `"message"` field with the error message.

## Main Function

The `main` function sets up the logger, initializes the Actix Web server, and binds it to the "127.0.0.1:8080" address.

## Debugging

`RUST_BACKTRACE=1 cargo run`

### Install

* run `./install.sh` to setup torchlib
* You should see a successful test like follows:

```bash
/usr/local/lib/libtorch already exists. Deleting it to proceed with the installation.
LIBTORCH is already in ~/.bashrc
LD_LIBRARY_PATH is already in ~/.bashrc
Checking if libtorch is installed correctly...
Verifying that C++ PyTorch API is functional...
-- Configuring done
-- Generating done
-- Build files have been written to: /workspaces/rusty-deploy/rtorchdist/build
Scanning dependencies of target check_pytorch
[ 50%] Building CXX object CMakeFiles/check_pytorch.dir/check_pytorch.cpp.o
[100%] Linking CXX executable check_pytorch
[100%] Built target check_pytorch
 0.2312  0.1560  0.2892
 0.1933  0.5848  0.6959
[ CPUFloatType{2,3} ]
C++ PyTorch API is functional. Installation complete.

```

### Test against Image

* The index route will give you an example to curl against.
* Selfcheck routes will self check functions

### More Testing

* You can run: `cargo test --test vision_tests` after you build to ensure PyTorch is actually working with Rust bindings
* You can run: `cargo test --test model_tests` which tests model loading or `cargo test --test model_tests -- --nocapture`

#### Notes

* Pre-trained model here generated by running inside a python virtualenv:  `./convertdl.py`
* Next verify the model works by running in virtualenv: `./verify.py`
* You can download checksummed models by running: ./sync-models.sh


text:
#!/bin/bash

declare -A model_urls=(
  ["resnet18.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/resnet18.ot"
  ["resnet34.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/resnet34.ot"
  ["densenet121.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/densenet121.ot"
  ["vgg13.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/vgg13.ot"
  ["vgg16.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/vgg16.ot"
  ["vgg19.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/vgg19.ot"
  ["squeezenet1_0.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/squeezenet1_0.ot"
  ["squeezenet1_1.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/squeezenet1_1.ot"
  ["alexnet.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/alexnet.ot"
  ["inception-v3.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/inception-v3.ot"
  ["mobilenet-v2.ot"]="https://github.com/LaurentMazare/tch-rs/releases/download/mw/mobilenet-v2.ot"
)

declare -A model_checksums=(
  ["resnet18.ot"]="your_resnet18_checksum"
  ["resnet34.ot"]="your_resnet34_checksum"
  ["densenet121.ot"]="your_densenet121_checksum"
  ["vgg13.ot"]="your_vgg13_checksum"
  ["vgg16.ot"]="your_vgg16_checksum"
  ["vgg19.ot"]="your_vgg19_checksum"
  ["squeezenet1_0.ot"]="your_squeezenet1_0_checksum"
  ["squeezenet1_1.ot"]="your_squeezenet1_1_checksum"
  ["alexnet.ot"]="your_alexnet_checksum"
  ["inception-v3.ot"]="your_inception_v3_checksum"
  ["mobilenet-v2.ot"]="your_mobilenet_v2_checksum"
)

models_dir="model"

mkdir -p "$models_dir"

for model_name in "${!model_urls[@]}"; do
  url="${model_urls[$model_name]}"
  file_path="$models_dir/$model_name"

  if [ ! -f "$file_path" ]; then
    echo "Downloading $model_name..."
    curl -sSL -o "$file_path" "$url"
  else
    local_checksum=$(sha256sum "$file_path" | awk '{print $1}')
    remote_checksum="${model_checksums[$model_name]}"

    if [ "$local_checksum" != "$remote_checksum" ]; then
      echo "Checksum mismatch for $model_name. Downloading updated file..."
      curl -sSL -o "$file_path" "$url"
    else
      echo "$model_name is up to date. Skipping download."
    fi
  fi
done

echo "All files have been downloaded or updated in the 'model' directory."