+ محدودیت زمان: ۱ ثانیه
+ محدودیت حافظه: ۲۵۶ مگابایت
---
در یک دشت سرسبز تعدادی خرگوش و یک روباه وجود دارد. خرگوشها **در طی هر سال** تعدادشان دوبرابر میشود و روباه نیز **در پایان سال** تعدادی خرگوش را شکار کرده و میخورد.
از شما میخواهیم با داشتن تعداد اولیه خرگوشها و تعدادی که هر سال توسط روباه خورده میشوند، جمعیت آنها را بعد از چند سال محاسبه کنید.
![Zootopia](https://quera.org/qbox/view/1whSpJkHOu/zootopia.jpg)
# ورودی
در اولین خط ورودی، تعداد اولیه خرگوشها ($P$) و تعداد خرگوشهایی که هرساله توسط روباه خورده میشوند ($L$) با یک فاصله داده میشود.
در خط دوم ورودی، انتهای سالی که جمعیت خرگوشها پس از آن مدنظر است میآید. ($Y$)
$$1 \le P, L, Y \le 10$$
تضمین میشود که در پایان سال اول، روباه همهی خرگوشها را نمیخورد.
$$ L \lt 2 \times P $$
همچنین تعداد خرگوشها در هیچ زمانی صفر یا منفی نمیشود.
# خروجی
در تنها خط خروجی، تعداد خرگوشهایی که در آخر سال $Y$ ام زنده هستند را چاپ کنید.
دقت کنید که در سال آخر نیز روباه $L$ خرگوش را میخورد و بعد از آن تعداد را محاسبه میکنیم.
# مثال
## ورودی نمونه ۱
```
5 2
1
```
## خروجی نمونه ۱
```
8
```
در اینجا ۵ خرگوش اولیه داریم که در پایان سال به ۱۰ تا میرسد، سپس روباه ۲ تا از آنها را میخورد.
در پایان ۱ سال، ۸ خرگوش باقیمانده داریم.
## ورودی نمونه ۲
```
3 1
4
```
## خروجی نمونه ۲
```
33
```
+ ۳ خرگوش در پایان سال اول به ۵ خرگوش میرسند.
+ در پایان سال دوم به ۹ خرگوش میرسند.
+ در پایان سال سوم به ۱۷ خرگوش میرسند.
+ در نهایت در پایان سال چهارم به ۳۳ خرگوش میرسند.
تیم کدکاپ برای تهیه کیبوردهای مرحلهی حضوری کدکاپ به مشکل برخورد کرده است. قضیه از این قرار است که این کیبوردها بسیار بیکیفیت هستند و کلیدهایشان زود خراب میشود. بعد از اینکه یک کلید خراب میشود با فشار دادن آن هیچ اتفاقی نمیافتد.
در واقع هر **کلید** روی کیبورد یک عمر مشخص دارد و عمر همهی کلیدهای یک کیبورد نیز با هم برابر است.
به تعداد بارهای قابل پذیرش قبل از خراب شدن یک کلید روی کیبورد، «عدد کیفیت» میگوییم. برای مثال ممکن است «عدد کیفیت» یک کیبورد برابر ۲ باشد، در این صورت هر یک از کلیدها ۲ بار کار میکند و پس از آن از کار میافتد.
یعنی در این کیبورد به ازای متن ورودی زیر:
```
Welcome to CodeCup7
```
متن زیر را دریافت میکنید:
```
Welcome to Cdup7
```
در اینجا چند تا از حروف خراب شدند؛ مثلاً `o` خراب شد و دیگر کار نکرد.
حال در این مسئله از شما میخواهیم که با داشتن «عدد کیفیت» یک کیبورد، مشخص کنید اگر کاربر بخواد با آن یک متن اولیه را وارد کنید به جایش چه متنی وارد میشود.
## توجه
+ این کیبوردها اصلا *Caps Lock* ندارند.
+ عجیب بودن این کیبوردها به اینجا خلاصه نشده. آنها فقط یک شیفت دارند.
+ کیبورد اساساً امکان نگه داشتن یک کلید را ندارد و مثلاً برای تایپ "aa" باید دوبار کلید a را فشرد.
+ کاربر اصلا هیچ اشتباه تایپی نمیکند و از backspace استفاده نمیکند.
+ این کیبوردها keypad ندارند.
![کیبورد](https://quera.org/qbox/view/ArHsBy6pDW/keyboard.jpg)
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/45366/download_problem_initial_project/157571/) دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
```
Keyboard
├── go.mod
├── go.sum
├── main.go
└── main_sample_test.go
```
در فایل `main.go` دو تابع تعریف شدهاند که شما باید آنها را کامل کنید:
### تابع NewKeyboard
این تابع قرار است یک شی از نوع *Keyboard* را مقداردهی کند و بازگرداند. ورودی این متد همان «عدد کیفیت» کیبورد است. ساختار داخلی *Keyboard* را نیز میتوانید به دلخواه طراحی و استفاده کنید.
```go main.go go
type Keyboard struct {
// TODO
}
func NewKeyboard(dure int) *Keyboard {
return &Keyboard{
// TODO
}
}
```
### تابع Enter
این تابع یک رشته ورودی میگیرد و مانند این است که کاربر آن رشته را تایپ کرده است. در خروجی چیزی که واقعا تایپ میشود را برگردانید.
دقت کنید که ممکن است برای یک کیبورد چندین بار عملیات تایپ انجام شود. همچنین ممکن است در طول تایپ یک کلمه یک کلید خراب شود و در ادامهی کلمه خراب باشد.
```go main.go go
func (keyboard *Keyboard) Enter(inp string) string {
// TODO
return ""
}
```
برای سادگی فرض کنید ورودی *inp* فقط از کارکترهای زیر تشکیل شده است:
+ حروف کوچک و بزرگ انگلیسی
+ اعداد انگلیسی
+ فاصله معمولی
+ کارکترهای خاص مقابل: `?!'`
# آنچه باید آپلود کنید
پس از پیادهسازی توابع خواسته شده، فایل `main.go` را آپلود کنید.
![Aang](https://quera.org/qbox/view/Qw4OHLuLDf/aang.jpg)
سرویس [*Gravatar*](http://gravatar.com/) یک سرویس جهانی برای نگهداری آواتار (عکس پروفایل) افراد در سطح اینترنت است. اغلب وبسایتها (مخصوصاً وبسایتهای وردپرسی) از این سرویس برای نمایش آواتار پیشفرض کاربران استفاده میکنند. در این وبسایت به هر آدرس ایمیل یک تصویر اختصاص مییابد. تصویری که افراد در این وبسایت آپلود میکنند، در همهی وبسایتهایی که از *Gravatar* استفاده میکنند قابل مشاهده است.
حسن یک *API* ساده برای تولید لینک تصویر افراد براساس آدرس ایمیلشان طبق [این راهنما](https://en.gravatar.com/site/implement/images/) نوشته بود و برای آن تعدادی تست نوشته بود، اما پس از چند روز، طی یک سانحه، کد پیادهسازیشدهی برنامه از سیستمش حذف شدند! او اکنون فقط تستهای برنامه را در دسترس دارد، اما دیگر حوصلهی پیادهسازی مجدد برنامه را ندارد. از شما میخواهیم این برنامه را برایش پیادهسازی کنید تا تستهایش پاس شوند.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/45366/download_problem_initial_project/157574/) دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
```
gravatar
├── handlers
│ └── gravatar.go
├── test
│ └── gravatar_sample_test.go
├── go.mod
├── go.sum
└── main.go
```
برنامه را بهگونهای پیادهسازی کنید که همهی تستهای سؤال پاس شوند.
# نکات
+ برای اجرای تستها، میتوانید از دستور `go test ./test/...` استفاده کنید.
+ تستهای اصلی سؤال دقیقاً همان تستهایی هستند که در پروژهی اولیه موجود است.
+ امکان اعمال تغییر در همهی فایلهای برنامه (بهجز تستها) را دارید، اما مجاز به اضافه کردن فایل جدید نیستید.
# آنچه باید آپلود کنید
پس از پیادهسازی برنامه، محتویات دایرکتوری اصلی برنامه را زیپ کرده و ارسال کنید، بهطوری که هنگام باز کردن فایل زیپ، فایل `main.go` (و سایر فایلها، در صورت نیاز) دیده شود.
![Do you remember?](https://quera.org/qbox/view/YvZopPEFmD/do-you-remember.jpg)
چیزی که از شما در این سوال میخواهیم، یک حافظهی نهان (*Cache*) از نوع کلید و مقداری است. این حافظهی نهان، اطلاعات را داخل دیسک نگهداری نمیکند و از نوع «داخل حافظهای» است.
تا اینجا احتمالاً ذهنتان سمت [*Redis*](https://redis.io/) رفته است، اما این حافظه به شکل خاص برای زبان *Go* طراحی شده و از امکانات این زبان بهره میبرد. به طور خاص برای اتصال از سمت کاربر به حافظهی نهان از چنلها استفاده میشود.
روال کار استفاده از حافظهی نهان را میتوان به این شکل خلاصه کرد:
+ یک کَش جدید ساخته میشود. این کش از کشهای دیگر که تا الان ساخته شدهاند مجزا است.
+ از کش درخواست یک اتصال جدید میشود. در نتیجه یک جفت چنل ارسال و دریافت برمیگردد.
+ با کمک *اتصال* خود میتوانیم به کش دستور دهیم، مثلاً یک کلید و مقدار دهیم تا ذخیره شود.
+ با کمک همین اتصال یا اتصالات دیگر میتوانیم با داشتن کلید خود، مقدار را به دست بیاوریم.
به جز `Get` و `Set` که قابلیتهای اولیهی یک حافظهی نهان هستند، یک عملیات دیگر نیز تعریف میشود به نام `SetWithRTL` که در اینجا *RTL* مخفف *Request to Live* است. این عدد برای هر کلید تعریف میشود و به معنی تعداد درخواستهای خواندن است که پس از ذخیره شدن مقدار آن کلید میتوانیم انجام دهیم تا نهایتاً آن کلید حذف شود. برای نمونه اگر یک *RTL* برابر با ۳ داشته باشیم، چنین اتفاقی میتواند بیفتد: پس از ۳ بار خواندن، آن مقدار از حافظه حذف شده است، بهگونهای که اصلاً وجود نداشته.
```
client: Set my_key my_val 3
client: Get my_key
server: my_val
client: Get my_key
server: my_val
client: Get my_key
server: my_val
client: Get my_key
server:
```
## جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/45366/download_problem_initial_project/157573/) دانلود کنید. ساختار فایلهای پروژه به صورت زیر است:
```
cache/
├── go.mod
├── go.sum
├── main.go
├── cache.go
└── main_sample_test.go
```
در فایل `cache.go` چند تابع وجود دارد که در `main.go` استفاده شدهاند و شما باید آنها را کامل کنید.
در فایل `main.go` که نمیتوانید محتوای آن را تغییر دهید، پیادهسازی سمت کاربر این حافظهی نهان آمده است و در فایل تست نمونه، نحوه استفاده از آن آمده است.
در فایل `cache.go` فقط امضای توابع آمدهاند. شما باید مطابق خواستهی صورت سؤال توابع را کامل کرده و در صورت نیاز توابع جدید اضافه کنید.
### فایل `main.go`
```go main.go go
func (conn *CacheClientConnection) Set(key string, val CacheVal) {
}
func (conn *CacheClientConnection) SetWithRTL(key string, val CacheVal, RTL int) {
}
func (conn *CacheClientConnection) Get(key string) CacheVal {
}
```
در این فایل همهی توابع پیادهسازی شدهاند و بر اساس این پیادهسازیها پروتوکل ارتباط بین کلاینت و سرور را مشاهده خواهید کرد. برای مثال، در صورتی که برای کلید مقداری وجود نداشته باشد، رشتهی خالی برمیگردد.
### فایل `cache.go`
```go cache.go go
type Cache struct {
}
type CacheServerConnection struct {
}
func InitCache() *Cache {
// TODO
return nil
}
func (cache *Cache) Connect(name string) *CacheClientConnection {
}
```
### تابع `InitCache`
در این تابع یک کش جدید ساخته شده و مقدار میگیرد و اشارهگر به آن برمیگردد. همچنین در صورت نیاز میتواند عملیات دیگری نیز انجام شود.
### تابع `Connect`
در این قسمت یک کاربر جدید با یک نام جدید (که تضمین میشود یکتاست) میخواهد به این کش متصل شود. برای این منظور یک جفت چنل درست میشود که در قالب یک `CacheClientConnection` به کاربر برمیگردد. همچنین باید این دو چنل برای خود کش نیز نگه داشته شوند تا بتوان به آنها گوش داد.
# آنچه باید آپلود کنید
پس از پیادهسازی توابع خواستهشده، فایل `cache.go` را آپلود کنید.
![دیتابیس](https://quera.org/qbox/view/TDVsCpIzw4/databases.png)
علی بهتازگی با کتابخانهی [*GORM*](https://gorm.io/) که یک *ORM* به زبان *Go* است آشنا شده. او داکیومنت این کتابخانه را مطالعه کرده و به مرور زمان در حال پی بردن به امکانات جذاب این کتابخانه است. از آنجایی که علی فرد کنجکاوی است، او تصمیم گرفته تا سورس کد این کتابخانه را مطالعه کند و دریابد که این کتابخانه چگونه کار میکند. از آنجایی که کد این کتابخانه بسیار طولانی است و علی حوصلهی خواندن آن را ندارد. به دنبال نسخهی سادهای از یک *ORM* مشابه *GORM* میگردد تا بتواند با خواندن سورس کد آن، نحوهی کارکرد آن را متوجه شود. از شما میخواهیم تا چنین کتابخانهای را برای علی پیادهسازی کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/45366/download_problem_initial_project/157570/) دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
```
orm
├── test
│ └── orm_sample_test.go
├── configurators.go
├── driver.go
├── go.mod
├── go.sum
├── orm.go
└── query.go
```
## استراکت `EntityConfigurator`
این استراکت در فایل `configurators.go` تعریف شده است. موجودیتهای برنامه میتوانند شامل اطلاعات مختفی نظیر نام جدول باشند. این اطلاعات میتوانند در این استراکت ذخیره شوند. متدی با نام `Table` برای این نوع داده تعریف شده است که فیلد `table` (نام جدول) مربوط به موجودیت را مقداردهی میکند. در صورت نیاز، میتوانید ساختار این استراکت را تغییر دهید، اما نباید امضای متد `Table` را تغییر دهید.
## استراکت `Driver`
قرار است *ORM* ما بتواند به دیتابیسهای مختلف متصل شود. درایورها، جزئیات پیادهسازی مربوط به *DBMS* های مختلف را مشخص میکنند. استراکت `Driver` در فایل `driver.go` تعریف شده است. همچنین یک استراکت بدون نام در متغیر `Drivers` تعریف شده که شامل دو درایور در قالب فیلدهای `PostgreSQL` و `SQLite3` است. نام درایور `PostgreSQL` را برابر با `postgres` و نام درایور `SQLite3` را برابر با `sqlite3` قرار دهید. یک فیلد با نام `PlaceHolderGenerator` نیز وجود دارد که مقدار آن یک تابع است که با دریافت تعداد پارامترهای موجود در یک کوئری *SQL* ، آرایهای از رشتهها که بیانگر *placeholder* مقادیر موجود در کوئری هستند را برمیگرداند. این تابع را برای دو درایور تعریفشده بهصورت زیر پیادهسازی کنید:
+ در درایور `PostgreSQL`، تابع `PlaceHolderGenerator` باید با دریافت `n`، مجموعهای از رشتهها به فرم `$i` (بهطوری که `i` از `1` تا `n` است) را برگرداند. برای مثال، اگر مقدار `n` برابر با `3` باشد، خروجی باید بهترتیب شامل رشتههای `$1`، `$2` و `$3` باشد.
+ در درایور `SQLite3`، تابع `PlaceHolderGenerator` باید با دریافت `n`، یک آرایه از رشتهها بهطول `n` که مقادیر آن همگی برابر با `?` هستند برگرداند.
## اینترفیس `Entity`
این اینترفیس در فایل `orm.go` تعریف شده و شامل یک متد `ConfigureEntity(e *EntityConfigurator)` است. موجودیتهای برنامه باید این اینترفیس را پیادهسازی کنند. متد `ConfigureEntity` این اینترفیس، پیکربندی اولیهی یک `Entity` را انجام میدهد (مثلاً نگهداری نام جدول مربوط به موجودیت) که به ازای هر `Entity` توسط برنامهنویس آن پیادهسازی میشود.
## استراکت `ConnectionConfig`
این استراکت شامل پوینتری به یک `sql.DB`، پوینتری به یک درایور و آرایهای از موجودیتهای برنامه است. از این `ConnectionConfig`ها در تابع `SetupConnections` استفاده میشود.
## تابع `SetupConnections`
این تابع با دریافت تعداد نامشخصی `ConnectionConfig`، اتصال مربوط به کانکشنها را برقرار میکند و مقداردهیهای اولیهی لازم برای کانکشنها را انجام میدهد.
## تابع `Find`
این تابع با دریافت کلید اصلی (عددی) یک موجودیت از نوع `T` (جنریک)، اطلاعات مربوط به سطر موردنظر از دیتابیس را باید در قالب یک آبجکت از نوع `T` برگرداند. نام ستونها در جدول بهصورت `snake_case` هستند، اما نام فیلدهای نوع دادهی `T` بهصورت `PascalCase` است. برای مثال، اگر در یک موجودیت، فیلدی با نام `BodyText` داشته باشیم، در جدول متناظر با موجودیت، ستونی با نام `body_text` وجود خواهد داشت. تضمین میشود که فیلدها برای ستونهای مختلف جدول تعریف شدهاند.
## تابع `All`
این تابع، اطلاعات مربوط به سطرهای جدول موردنظر از دیتابیس (با توجه به نوع دادهی جنریک) را باید در قالب آرایهای از `T`ها برگرداند.
## تابع `First`
این تابع باید اطلاعات اولین سطر (بر اساس کلید اصلی جدول) در جدول موردنظر (با توجه به نوع دادهی جنریک) را در قالب آبجکتی از نوع `T` برگرداند.
## تابع `Last`
این تابع باید اطلاعات آخرین سطر (بر اساس کلید اصلی جدول) در جدول موردنظر (با توجه به نوع دادهی جنریک) را در قالب آبجکتی از نوع `T` برگرداند.
## استراکت `QueryBuilder`
این استراکت شامل اطلاعات یک کوئری برای تبدیل به فرمت *SQL* است که در فایل `query.go` تعریف شده. آن را به دلخواه پیادهسازی کنید.
تابع `NewQueryBuilder` را بهگونهای پیادهسازی کنید که یک نمونهی جدید از نوع `QueryBuilder` برگرداند.
متدهای زیر برای استراکت `QueryBuilder` تعریف شده که باید آنها را پیادهسازی کنید:
+ متد `Get`: این متد باید کوئری مدنظر (با توجه به متدهای فراخوانیشده روی `QueryBuilder`) را اجرا کرده و نتیجه را در قالب یک آبجکت از نوع `OUTPUT` (بهصورت جنریک) برگرداند. در اینصورت، تنها اولین نتیجه برمیگردد.
+ متد `All`: این متد باید کوئری مدنظر (با توجه به متدهای فراخوانیشده روی `QueryBuilder`) را اجرا کرده و همهی نتایج را در قالب آرایهای از `OUTPUT`ها (بهصورت جنریک) برگرداند.
+ متد `OrderBy`: این متد با دریافت نام یک ستون و نحوهی ترتیب (`ASC` یا `DESC`، بهمعنای صعودی یا نزولی) باید یک بخش `ORDER BY` به کوئری اضافه کند.
+ متد `Where`: این متد میتواند حداقل دو آرگومان دریافت کند:
+ اگر دو آرگومان به این متد پاس داده شود، اولین آرگومان نام یک ستون و دومین آرگومان مقدار مدنظر خواهد بود. شرط برابر بودن مقدار ستون موردنظر با مقدار واردشده باید به کوئری اضافه شود. در صورتی که شرطی از قبل به کوئری اضافه شده باشد، شرط فعلی باید با شرطهای قبلی `AND` شود.
+ اگر سه آرگومان به این متد پاس داده شود، اولین آرگومان نام یک ستون، دومین آرگومان یک عملگر (نظیر `=`) و سومین آرگومان مقدار مدنظر خواهد بود. شرط واردشده باید به کوئری اضافه شود. در صورتی که شرطی از قبل به کوئری اضافه شده باشد، شرط فعلی باید با شرطهای قبلی `AND` شود.
+ اگر بیش از سه آرگومان به این متد پاس داده شود و آرگومان دوم برابر با رشتهی `IN` باشد، آرگومان اول برابر با نام ستون موردنظر خواهد بود. آرگومان سوم به بعد، مقادیری هستند که مقدار ستون موردنظر باید حداقل برابر با یکی از آنها باشد.
+ متد `WhereIn`: عملکرد این متد، مشابه حالت سوم متد `Where` است.
+ متد `AndWhere`: عملکرد این متد، مشابه متد `Where` است.
+ متد `OrWhere`: عملکرد این متد، مشابه متد `Where` است، با این تفاوت که اگر شرطی از قبل در کوئری موجود باشد، شرط جدید با شرطهای قبلی `OR` خواهد شد.
+ متد `Limit`: این متد با دریافت یک عدد، *limit* موجود در کوئری را مشخص میکند.
+ متد `Offset`: این متد با دریافت یک عدد، *offset* موجود در کوئری را مشخص میکند.
+ متد `Table`: این متد با دریافت نام جدول در قالب یک رشته، نام جدولی که کوئری باید روی آن اجرا شود را مشخص میکند.
+ متد `GroupBy`: این متد با دریافت تعداد نامشخصی رشته، یک عبارت *group by* به کوئری اضافه میکند. در صورتی که این متد چند بار فراخوانی شود، ستونها باید بهترتیب به بخش *group by* کوئری اضافه شوند.
+ متد `Select`: این متد با دریافت تعداد نامشخصی رشته، مشخص میکند که مقدار کدام ستونها از جدول دیتابیس دریافت شوند. اگر این متد فراخوانی نشود، همهی ستونها (`*`) باید از جدول موجود در دیتابیس دریافت شوند. اگر این متد چند بار فراخوانی شود، ستونها باید بهترتیب به کوئری اضافه شوند.
+ متد `SetDriver`: این متد با دریافت یک `Driver`، باید موارد موردنیاز برای کوئری بیلدر (نظیر `PlaceHolderGenerator`) را به موارد موجود در `Driver` تغییر دهد.
+ متد `ToSql`: این متد باید کوئری *SQL* تولیدشده را در قالب یک رشته و مقادیر پارامترهای موجود در کوئری را در قالب آرایهای از `interface{}`ها برگرداند. اگر اولین فراخوانی روی یک آبجکت جدید `QueryBuilder` مربوط به متد `ToSql` بود، مقدار خروجی `error` را برابر با یک رشتهی غیر از خالی قرار دهید. در غیر اینصورت، `error` را `nil` برگردانید.
**نکته:** در صورتی که چند شرط `AND` و `OR` شوند، صرفاً کافی است تا آنها را با استفاده از عملگرهای مربوطه (بین پرانتز) کنار یکدیگر قرار دهید. مثال:
```sql
WHERE cond1 OR cond2 AND cond3 OR cond4
```
کوئریهای تولیدشده توسط `QueryBuilder` باید مطابق با مثالهای زیر باشند (فاصله و کوچکی و بزرگی حروف مهم است):
### مثال ۱:
```go
s := orm.NewQueryBuilder[Dummy]()
s.Table("users")
s.ToSql()
```
کوئری ایجادشده:
```sql
SELECT * FROM users
```
### مثال ۲:
```go
s.Table("users").SetDriver(orm.Drivers.SQLite3).
Where("age", 10).
AndWhere("age", "<", 10).
Where("name", "CodeCup").
OrWhere("age", ">", 11)
.ToSql()
```
کوئری ایجادشده در درایور *SQLite3*:
```sql
SELECT * FROM users WHERE age = ? AND age < ? AND name = ? OR age > ?
```
### مثال ۳:
```go
orm.NewQueryBuilder[Dummy]().
SetDriver(orm.Drivers.PostgreSQL).
Table("users").
WhereIn("id", 1, 2, 3, 4, 5, 6).
ToSql()
```
کوئری ایجادشده در درایور *PostgreSQL*:
```sql
SELECT * FROM users WHERE id IN ($1, $2, $3, $4, $5, $6)
```
# نکات
+ توجه داشته باشید که در این *ORM* شما باید مستقیماً با دیتابیس در ارتباط باشید و امکان هندل کردن دادهها بهصورت *in-memory* را ندارید.
+ در صورت نیاز، میتوانید فایلهای جدیدی در برنامه تعریف کنید، اما امکان ایجاد دایرکتوری جدید را ندارید.
+ میتوانید از کتابخانههای *3rd party* در کد خود استفاده کنید. در اینصورت، باید فایلهای `go.mod` و `go.sum` را نیز در فایل زیپ ارسالی خود قرار دهید.
# آنچه باید آپلود کنید
پس از پیادهسازی برنامه، محتویات دایرکتوری اصلی برنامه را زیپ کرده و آپلود کنید، بهطوری که وقتی آن را باز میکنیم، با فایل `driver.go` و سایر فایلهای برنامه مواجه شویم.