// File: contracts/safari/whitelist.sol



// SPDX-License-Identifier: MIT LICENSE

pragma solidity ^0.8.0;

contract SafariOGWhitelist {
  bytes internal offsets = hex"0000001800300048005a006600780084009600ae00f001080108010e01500168019201b001c801ec01f8020402100228025e027c0294029a02b202d002e202e803000306030c032a034203540366037e039003b403de04140432044a047a049204b004ce04f205280546056a058e05ac05ca05f4061e0630067206a206ba06cc06de06fc0714072c073e0756075c076e0780079e07c807ec080a08160828085208700882088e08a008b808e208e808fa090c09240942095a0978098a09ae09ea09f00a080a1a0a260a380a3e0a560a680a7a0a9e0abc0ae00aec0b040b220b4c0b640b8e0bac0bc40bdc0bf40c120c360c420c540c600c720c900ca20cb40ccc0cde0cf60d200d2c0d3e0d5c0d620d9e0daa0dce0df20dfe0e1c0e280e400e580e6a0e7c0ea60ec40edc0efa0f060f1e0f3c0f600f900fc00fde1002101a1038104a10621080109810a410c210d410e010ec10fe111611461164118e11b211d611f4121212301254128412ae12cc12f0132c133e135c136e1386139813aa13bc13da1404141c143a14521464147c148814a614b814d014f414fa1506151815361560158a15ba15cc15e416021632164a166e1680169e16b616c216ce16ec170a17161740174017461764178e17ac17ca17fa18181824183c1860189018a818ba18e4190e192c193e19501974198c19b019da19fe1a281a461a641a761a9a1aac1abe";

  bytes internal items = hex"0000000000041b26000000018ca400000003fcdd00000007337300000002353c000001009e5c00000104caaf00000100045b0000000128850000050f35ba00000001736d00000102004900000001893300000005d0f900000001cd3800000001ebe800000001005000000001235500000001379b00000001737000000109cd18000000023f0d00000005538a00000628d7d70000000215910000010535bf000000063ff900000001b071000000030deb000000012700000002082dfe000000015647000000075f330000000460ca0000000373c2000000068f5c00000100bde20000020be3b50000000ce4800000000107bd00000001465700000003848400000003fb20000006190ac7000000014640000000025eeb0000000273aa0000010b77c6000000017c7100000005804e000001038b3a00000001cf5700000006d18200000004da9900000200df260000010968ec00000002815c00000102916100000001b50c000000015e9900000100600500000001817600000003932500000014e82600000202e8590000060eeb6e000000040825000000010f5b00000002a78700000101c89f00000001d353000000012b3200000001a54d00000001e46500000002ef200000010843d00000000454f4000000015e5100000001adc000000003b11500000002c782000000025d02000001026db700000002246f00000004a74800000002093000000001e8750000010927990000010539490000000549e200000002f2ba0000000221e40000000350ca0000000267350000000370c6000000017a6800000007b64c00000004c6e900000001c73800000205d41100000003494f0000010454fe00000001996200000005bd7c00000003e25a0000000422dd00000002391d00000004b0cc00000201b446000000057bd900000001293700000d85a90d0000073aabe300000003f3a7000001081f6b000000012943000000032b72000000053c3600000100c3190000000aa5b800000001c30100000b32cc7e000000042316000000051041000000018c3d000000018cef0000010392c00000010ae4b2000000048d560000010f62b7000000027b68000000038f9f00000001d82900000001fc3100000101851700000001a17f00000001b2db00000007c1e1000000079a49000000029f9300000003e5ed00000002044000000001b90700000001cd0c00000001631e00000103b02000000002deb000000004f640000001019ea000000003b75500000007c5930000000505dc0000020208f000000101a69f00000001b4d600000009dcf600000001e76a000000010ded0000000232b300000001480300000105c9e000000001e76a00000307e9b200000003fdb00000000400f6000000050e4e00000001140600000001193c000000034040000000025ba60000000c849a00000003c93e00000009ebaa0000010411300000000136100000010367200000010277fb0000000a83ae0000000519bc0000000196e400000002c41100000103f00e0000020332f0000000014eb9000000026cea000000017e60000002048e6c00000003a65e00000100bacd00000001f65f000000052673000000023c4100000003c20600000003ea47000000010e040000000216040000000350f500000001c06600000101f0260000000155b4000002307e7900000005904600000002a21e00000002fcc80000000106f400000102171d000003066a8e000000019e9200000001c4eb00000111ddf4000000010562000000020beb0000010913f50000000114550000000138bf000000016696000000016c500000010a707400000001e37a0000000100ce0000000239c20000000662c90000000ca10700000205e2a8000000044e2a000000015f2d00000005996600000003a08200000002b5f300000003f3600000010a34b200000004721500000102bd1700000003bd320000020bbe2700000104e94d000001047fc700000001852d00000104961500000106b73f00000003be2b000000011b4b00000208294500000001a8f800000001c9eb00000200e22900000001439f0000020344d4000000074ca5000000035ce10000000283550000000286c500000109abad00000100168900000204206000000002486100000002542e000000016b8e00000002970c000000019b6700000001136400000003693500000306f7f80000000221d700000005639b0000000269be0000010090c900000003977a000000029f2f00000100af3400000412b62700000002bd8d00000001c44100000002eff80000000105e60000010013c2000000012b07000000062c57000001004cad0000050b5c9700000002702500000105dd9b0000000177f9000001017fa0000000029f18000000019f92000000018f0b00000007e98d00000002fa38000000032ca7000000026e0800000003aaa60000000331490000000146d3000000014c5e00000001711b0000072fee01000000011c4b000000062be8000000013c0a00000001bba8000000011ab90000010521e200000102bdb500000002f34400000212547b00000002691300000001db5d000000031029000000012dcc00000109c5db00000003cabd00000108ce930000010405d80000000289a800000004f41d00000004326e0000010143e50000000167d4000000012f9b000000023b610000000b920e000000029a0f00000228e148000001123b360000000349d40000010c7dfb00000001819300000002918d000000049b9100000001cfba00000003124c000001027e8d00000109915e00000208b6c000000206eb5000000102feff0000010032f80000000539cd0000000169be000000029a1600000002aa9200000002618d00000101888e0000010442d600000003c67500000311ce05000002001e7f000001012cb200000205613500000002743600000003a01d00000001a11100000009e83c000000032404000000016b8200000006aff700000109b8b700000001f33d000000040e4300000313368600000107f92a00000100c2bc00000004ef2b000000041a19000000035a52000000056f78000000032ff6000000018d4d00000003cfa100000002da6f00000100349c0000000138f00000000855760000000159f0000000016a110000000192ac00000001f2eb00000102e1740000000710f1000000021b5500000100543700000002021800000005145000000001e259000000011a9c000000011bd400000207a49d00000001d70b0000000305e9000000021f8a0000020f8e1600000002b27200000002e0d00000000117fb000000039b6400000002cba600000001f75200000002049c0000031b17d1000000044f870000010374c300000203b054000000043311000000024dda000000017a9f0000010f17c2000000012c6e0000001e7a4b00000001b60200000001e37d00000001ec6e00000004067900000001070c000000010c3d000001115c8100000004ae8500000002b27c00000001b2b000000003b5dc00000006e2c700000002e83200000104d8dc0000000430a0000000044cb80000000150270000000173b70000020175cc000000017ea900000002933d00000001222900000103a15000000004035f0000000938e800000001c8e10000010444ff000000053bcd000000025d5200000001dcf700000003ea52000000028e960000000e9035000000039e2500000001159a000001019be800000101c30900000002372a000001123cb40000000c419f000000018ad500000001ae4400000102b53a000000023285000000013969000001013a0f000001026f11000002098ce70000000103fd000000010d47000001003bf1000003123c1c00000001c5d10000000dcb80000000020dd3000000012b43000000010811000000013da3000000015cbf00000100712f0000000384ab00000102877a000000038e51000001029b0000000207a248000000014dcb000001035349000000018a5b0000040898af00000102d11800000001db820000010aef09000001004f7a00000005b9a800000104e19a00000205e97d0000000407d3000001070a4d000000031ab500000002a5e000000002a85100000004cdc500000001e4d7000000085766000000056031000001027bb9000000029aec00000001ab7a000000015062000000027f2500000006c33100000001d6930000090a3f4d000000034cab00000104854c0000000b91970000000635a000000001a26800000104e6ce00000002f0e6000000011aac00000002594b00000002915e0000020693f900000002eb44000002012f37000002034abe000000019beb00000001c69400000117daf100000002f2bf0000000129a500000002e276000000020fa60000011559c700000003eae40000000101cc0000000214cf000001031ded00000004a76500000629eaf4000000015b24000000016be90000020c8e5000000001adca00000001ea5800000205689e00000005982c000001059a70000000023fbf00000001737400000003a8dd00000001b20800000001c68a00000100f0f100000001f61c000004151c1e00000001648e0000000a915c000000036b0e000000037d370000000295d700000001eb13000001062763000000032efa000000013d9a00000104665900000200729700000001c48700000005cd1b000003000da40000020be8e9000001051dcd000000024b4700000001c1ba00000002314400000001451d00000002571400000200e0f900000002ecbe00000001ccac0000000300b4000000012007000000015d1f0000010260fc00000004649d00000001688000000101781d000002107d2f000000017ec3000000028c8100000001483200000003d96700000002004a0000010418d0000000028d740000000292c8000000019bfc00000001b9520000000102820000000118f0000001004c43000000025a37000000039caf00000008aa0d00000005362700000003fb3a0000010231ce00000001732c00000002d98300000001ea6700000002ec84000000018d2c00000002aa11000000032b7900000001d98800000202e16b00000001fe9c00000003182f00000101a3dc00000001aaf800000002e76e000001006da000000002913600000001c622000000021df200000003477d00000002c400000000011ff20000000340e3000000038e940000000193f300000002b08500000004cda40000010cf34a000002021c0b0000020620ae00000001756200000004b1bd00000002e9b9000000011318000000022dc50000000384f200000002e11c000001013a4800000101446f00000003979700000001c1cd00000001f04100000001012c00000003896d000000010aea00000002233400000002a3420000040bf91e0000000160f40000000174bf00000001916e00000216b10f00000001bf8d000000012e09000004114b18000000019ed400000103b9cf00000102fdf300000002ff0800000001010a000000046e1d0000023e834100000108881c0000000a9e5800000001aa2f00000001c46100000002ec060000000127b8000000012d3e000000074dbc0000000175a600000001796000000201a98500000002d3c000000001e94700000001330c0000000a5932000000015aec000000016a4000000007e550000001024ca7000000054f9b00000103530500000003621000000001cfae0000000ee7a9000000014f7d000000016f7900000002c73c00000009e120000002028d7a00000001b3350000000fbb0500000001d81900000001d915000000012909000000012f310000020971b100000001196b00000205593d00000001ee1d00000104efdc00000b0006130000000153dc000000019ecd00000101cff300000007ee0b00000100449b00000004cc9f00000001d06f00000103ea5500000005285600000102e567000000011e460000000132b0000001019b0700000100adc800000002b4910000010e622200000102887100000100890f0000000a45b4000000038e15000000039e1000000009f034000000010935000001090d78000000041feb000000034db100000001525900000002c86c00000002f9c00000000106e20000010051300000000454fa000006076ea50000000196a5000005009d3900000003a3a500000003e3090000000101110000000525750000000730340000000134d60000000187090000040c159c000000014b880000000167720000010282170000000194e000000108a57300000001ca38000000013904000000014cc90000000177d500000001b04000000003c35400000006c3c100000101926000000005c29d00000001da870000000ade7500000001ef6700000001f605000000021cc50000000551720000030c84b600000001c4cb00000002fc8b0000010300dd000000012ac8000000019dc800000001a2cf00000001acf00000020410c300000100bd9400000307c75400000103e0f700000001f9640000010105be0000020440220000000254d500000001b8fb00000008c7ff00000106f40e00000200557b000000025bba0000000161b500000001918000000003ab080000000ac95400000005d6db00000002e0240000000372d5000000017678000000018ef900000001b12400000005cf3c00000001e8bb00000104f5d800000005243a0000000134f600000003800700000006915000000005bc660000000104f30000000143c9000000048a8e000000029e3800000103cae000000001d5af0000000303110000010107c600000004132300000006358f000000028f0f00000204a41700000101af430000020bbc1400000002dc1c00000007ff76000000014580000000107ac300000112e6a30000000143670000020555ee000000057d1300000003bc870000020df43e00000002264100000001b64500000009c708000000014c5800000106a39e00000005aac900000009d171000000022f380000010095d600000002be9b00000005743000000104c10200000001d37e000000022e3d000000065bea000001039bc3000004011dac0000000175f300000002c7e300000004f7c200000002fc3d000001032bdb0000000136c0000000015c2500000001a97700000001add300000002e8cd00000001fac60000000444800000001a65960000000869ef000000018d5000000105390800000002889700000003978400000008d10900000009e7c3000004110840000002082f1200000109342400000103fd9500000001cac600000105dcdc00000200e85a0000000181c0000001019c0300000203a97700000003b43a000009120b17000000017cfd000000010f53000000015d32000000037c5800000003c50100000002d1b40000000acb1800000101cec700000001e69d000000012eff000000036f3d000000018a850000010d8b1f0000040938d00000000340d8000005029d8100000002a7e600000100c47a00000003cb7200000001eb3c0000021220a600000108b4bd00000001134f00000015bf8300000001f46e0000000320ae0000010951aa00000005a4e300000103cb7200000002f7aa000000013b55000000033b89000000015e8200000305988200000105baf000000006c75900000001e5cb0000020226eb0000041939e200000001ceca00000008d99300000001e1f300000003fb2a0000000efcc8000000015600000000025cf0000001018f0100000205a2d300000200a56f00000001b39700000001be4300000003d9160000010168b5000001007b140000000185f1000000026b92000000036f800000000aeb6400000001f5ff000001081a54000001042c2d0000030933fb00000001932200000208d14a0000000108ab000000011e9700000003258c000000014a310000020ea7710000000bbb8d00000005ce0e00000001f07f0000000312ed00000001a35a00000100ced700000004df88000001081920000000093ef80000000143490000000167700000000197aa000000019b6c0000000206c4000000059b8300000002abba0000000123d100000002602d00000104d72700000002e0f500000004f685000000011de1000000012a6200000005bc0f00000210fac2000000013506000000016b63000000022dc600000001a9c90000000108d0000000011e160000040c797c00000001b47b00000007dd690000000209ba0000010622410000000358c700000003798d0000000dd94d000000044bbf00000005e7b30000000106c300000001191700000005513500000004754500000001d4c600000003f93f00000003ff740000000213f80000010311e00000000155170000000157160000010ab42e00000008cf56000000010f0c0000000128bf000000023300000001107db000000001a5a900000002ac2d00000001b52e000002064c9800000106906d00000101a81700000001c3a30000000ec8ca0000010216a8000002193bd90000031a40fc00000001714900000001750c000000010101000003090d1600000006125f00000002551e00000101bb8300000001c76500000001e0fa00000300fa960000000107f600000001417f000000025041000001075dbd00000002b28c000000013ed900000103a5f4000000010b0500000107b13200000103cf6c00000001fe5500000002077b000001010c4e00000001950200000003c8a600000001e7e200000001f43800000021393f000010794a27000000039ea300000001a03300000001b28500000005d68c00000001e47600000001feb90000020c1dbb00000001223b00000001cf5d00000003d27c00000001589900000002d28900000002e96300000001109800000105263b000000052d22000000014da10000010475290000000490dd00000005c19d0000000439c3000001033d32000000019d2e00000001aa9e00000116f17800000001f23800000001fa530000000158e90000000f88ea00000003943100000004a9f500000103c9b00000000107b3000000010e7c00000003aec60000010a1fc100000001307d00000002fbb2000000014eab0000000550870000000266bf00000002676e000000016e530000020fbdc10000000100c900000001102500000105e19f00000317fd65000001031f90000000014386000000017e7900000002aee100000002cc0500000001edbb000000010a18000000012601000001002b15000000014ac000000001c79600000001ce6b00000107d4810000000124ae000000043def0000010545e6000000044cf600000002d0a400000119fc5b0000000116710000021542f30000000a5d7b000001068c2c00000002b0b300000004cdd300000001f3280000000124480000000173950000000189ec000000029f2000000004a6140000000a112600000001273a000000017c4c000000018c6500000001c393000000037bc9000000019e0d00000100d51c0000010522670000000270330000010271500000000395c4000000059bf800000001cd5000000002aa5c00000001dbe300000003dca5000000045ec300000001e14600000005fd3200000001";

  address public owner;
  address public minting_contract;

  constructor() {
    owner = msg.sender;
  }

  function setOwner(address addr) external {
    require(msg.sender == owner, 'you are not the owner');
    owner = addr;
  }

  function setMintingContract(address addr) external {
    require(msg.sender == owner, 'you are not the owner');
    minting_contract = addr;
  }

  function balanceOf(address holder) external view returns(uint256) {
    uint8 bought;
    uint8 claimed;
    uint8 lions;
    uint8 zebras;
    (bought, claimed, lions, zebras) = this.getInfo(holder);
    return uint256(lions) + uint256(zebras);
  }

  function getInfo(address holder) external view returns(uint8 number_bought, uint8 number_free_claimed, uint8 lions_owned, uint8 zebras_owned) {
    uint16 first = uint16(uint160(holder) >> 152 & 0xff) * 2;
    uint16 addr_part = uint16(uint160(holder) >> 136);

    uint256 slot0 = 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563;
    uint16 offset;
    uint16 end;

    assembly {
	offset := and(shr(mul(sub(30,mod(       first,32)),8),sload(add(slot0,div(       first,32)))),0xffff)
	end :=    and(shr(mul(sub(30,mod(add(first,2),32)),8),sload(add(slot0,div(add(first,2),32)))),0xffff)
    }

    for (; offset < end; offset += 6) {
      if (uint16(uint8(items[offset]) *256 + uint8(items[offset+1])) == addr_part) {
        return (uint8(items[offset+2]),uint8(items[offset+3]),uint8(items[offset+4]),uint8(items[offset+5]));
      }
    }
    return (uint8(0),uint8(0),uint8(0),uint8(0));
  }

  function getInfoPacked(address holder) external view returns(uint256) {
    uint256 first = uint16(uint160(holder) >> 152 & 0xff) * 2;
    uint256 addr_part = uint16(uint160(holder) >> 136);

    uint256 slot0 = 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563;
    uint256 slot1 = 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6;
    uint256 offset;
    uint256 end;

    assembly {
	offset := and(shr(mul(sub(30,mod(       first,32)),8),sload(add(slot0,div(       first,32)))),0xffff)
	end :=    and(shr(mul(sub(30,mod(add(first,2),32)),8),sload(add(slot0,div(add(first,2),32)))),0xffff)
    }

    uint256 result;

    assembly {
      let i := and(offset, 0xffff)
      let e := and(end, 0xffff)

      let slot_index := add(slot1, div(i, 32))
      let slot_val := sload(slot_index)
      let shift_amount := mul(sub(30, mod(i, 32)), 8)
      let this_addr_part

      for { } lt(i, e) { } {
        this_addr_part := and(shr(shift_amount, slot_val), 0xffff)

        switch eq(this_addr_part, addr_part)
	case true {  // found the address
	  result := shl(32, i)

	  switch shift_amount
	  case 0 {
	    slot_val := sload(add(slot_index, 1))
	    result := add(result, shr(224, slot_val))
	  }
	  case 16 {
	    result := add(result, shl(16, and(slot_val, 0xffff)))
	    slot_val := sload(add(slot_index, 1))
	    result := add(result, shr(240, slot_val))
	  }
	  default {
	    result := add(result, and(shr(sub(shift_amount, 32), slot_val), 0xffffffff))
	  }
	  let p := mload(0x40)
	  mstore(0x40, add(mload(0x40), 0x20))
	  mstore(p, result)
	  return(p, 32)
	}

	case false {  // this_addr_part != addr_part
	  i := add(i, 6)

	  switch gt(shift_amount, 32)
	  case true {
	    shift_amount := sub(shift_amount, 48)
	  }
	  case false {
	    slot_index := add(slot_index, 1)
            slot_val := sload(slot_index)
	    shift_amount := add(208, shift_amount)
	  }
	}
      }
    }

    revert('you are not in the whitelist');
  }

  function setBoughtAndClaimed(uint16 offset, uint16 bought_and_claimed) external {
    require(msg.sender == minting_contract, 'you are not the minting contract');

    items[offset+2] = bytes1(uint8(bought_and_claimed >> 8));
    items[offset+3] = bytes1(uint8(bought_and_claimed));
  }
}